From d88aa214062dc3fa5814dfd3f972c579cfbd7c00 Mon Sep 17 00:00:00 2001 From: Igor Pellegrini Date: Tue, 2 Aug 2016 13:37:35 +0000 Subject: [PATCH] Add renderer for KeyValueList attributes Issue #19 --- .../HtmlKeyValueListAttributeRenderer.php | 123 ++++++++++++++++ .../attribute/key-value-list/as_input.twig | 132 ++++++++++++++++++ .../key-value-list/as_itemlist_item_cell.twig | 17 +++ pub/static/themes/whitelabel/_lib.scss | 1 + .../components/field/_components.field.scss | 20 +++ .../_components.key-value-list.scss | 42 ++++++ 6 files changed, 335 insertions(+) create mode 100644 app/lib/Ui/Renderer/Html/Trellis/Runtime/Attribute/KeyValueList/HtmlKeyValueListAttributeRenderer.php create mode 100644 app/templates/html/attribute/key-value-list/as_input.twig create mode 100644 app/templates/html/attribute/key-value-list/as_itemlist_item_cell.twig create mode 100644 pub/static/themes/whitelabel/components/key-value-list/_components.key-value-list.scss diff --git a/app/lib/Ui/Renderer/Html/Trellis/Runtime/Attribute/KeyValueList/HtmlKeyValueListAttributeRenderer.php b/app/lib/Ui/Renderer/Html/Trellis/Runtime/Attribute/KeyValueList/HtmlKeyValueListAttributeRenderer.php new file mode 100644 index 00000000..a0a4eec2 --- /dev/null +++ b/app/lib/Ui/Renderer/Html/Trellis/Runtime/Attribute/KeyValueList/HtmlKeyValueListAttributeRenderer.php @@ -0,0 +1,123 @@ +getOption('view_scope', 'missing_view_scope.collection'); + if (StringToolkit::endsWith($view_scope, 'collection')) { + return $this->output_format->getName() . '/attribute/key-value-list/as_itemlist_item_cell.twig'; + } + + return $this->output_format->getName() . '/attribute/key-value-list/as_input.twig'; + } + + protected function getTemplateParameters() + { + $params = parent::getTemplateParameters(); + + $params['allowed_keys'] = $this->attribute->getOption(KeyValueListAttribute::OPTION_ALLOWED_KEYS, $this->getOption('allowed_keys', [])); + $params['allowed_values'] = $this->attribute->getOption(KeyValueListAttribute::OPTION_ALLOWED_VALUES, $this->getOption('allowed_values', [])); + + if ($this->attribute->hasOption(KeyValueListAttribute::OPTION_ALLOWED_PAIRS) || $this->hasOption('allowed_pairs')) { + $params['allowed_pairs'] = $this->getOption( + 'allowed_pairs', + $this->attribute->getOption(KeyValueListAttribute::OPTION_ALLOWED_PAIRS, []) + ); + if ($params['allowed_pairs'] instanceof SettingsInterface) { + $params['allowed_pairs'] = $params['allowed_pairs']->toArray(); + } + $params['allowed_keys'] = array_keys($params['allowed_pairs']); + $params['allowed_values'] = array_values($params['allowed_pairs']); + } + + + $params['hide_pair_labels'] = $this->getOption('hide_pair_labels', false); + $params['key_maxlength'] = $this->getOption('key_maxlength'); + $params['value_maxlength'] = $this->getOption( + 'value_maxlength', + $this->attribute->getOption(KeyValueListAttribute::OPTION_MAX_LENGTH) + ); + $params['value_type'] = $this->getOption( + 'value_type', + $this->attribute->getOption(KeyValueListAttribute::OPTION_VALUE_TYPE, 'text') + ); + switch ($params['value_type']) { + case 'integer': + case 'float': + $params['value_type'] = 'number'; + $params['min'] = $this->getOption('min', $this->attribute->getOption(KeyValueListAttribute::OPTION_MIN_VALUE)); + $params['max'] = $this->getOption('max', $this->attribute->getOption(KeyValueListAttribute::OPTION_MAX_VALUE)); + break; + case 'boolean': + // @todo implement + // break; + default: + $params['value_type'] = 'text'; + break; + } + + $params['attribute_value'] = $this->normalizeAttributeValue( + $params['attribute_value'], + [ + 'key_maxlength' => $params['key_maxlength'], + 'value_maxlength' => $params['value_maxlength'] + ] + ); + + return $params; + } + + protected function determineAttributeValue($attribute_name, $default_value = []) + { + $value = []; + + if ($this->hasOption('value')) { + return $this->getOption('value', $default_value); + } + + $value_path = $this->getOption('attribute_value_path'); + if (!empty($value_path)) { + $value = AttributeValuePath::getAttributeValueByPath($this->getPayload('resource'), $value_path); + } else { + $value = $this->getPayload('resource')->getValue($attribute_name); + } + + if (!is_array($value)) { + throw new RuntimeError('Attribute value is not an array of key/value pairs.'); + } + + if ($value === $this->attribute->getNullValue()) { + return $default_value; + } else { + return $value; + } + } + + protected function normalizeAttributeValue($attribute_value, $options) + { + // @todo Purge invalid keys/values + // Purge values not compliant wth other options + + if (isset($options['key_maxlength']) && is_numeric($options['key_maxlength'])) { + foreach ($attribute_value as $key => $value) { + unset($attribute_value[$key]); + $attribute_value[substr($key, 0, $options['key_maxlength'])] = $value; + } + } + if (isset($options['value_maxlength']) && is_numeric($options['value_maxlength'])) { + foreach ($attribute_value as $key => $value) { + $attribute_value[$key] = substr($value, 0, $options['value_maxlength']); + } + } + + return $attribute_value; + } +} diff --git a/app/templates/html/attribute/key-value-list/as_input.twig b/app/templates/html/attribute/key-value-list/as_input.twig new file mode 100644 index 00000000..275fd19c --- /dev/null +++ b/app/templates/html/attribute/key-value-list/as_input.twig @@ -0,0 +1,132 @@ +{%- block field -%} {# this block exists for whitespace control #} +{%- set errors = errors | default([]) -%} +
+
+
+ +
+
+
+ {% if attribute_value is iterable %} +
+ + +
+
+ {% for key, value in attribute_value %} +
+
+ {% if allowed_keys is empty or readonly %} + + {% else %} + + {% endif %} +
+
+ {% if allowed_values is empty or readonly %} + + {% else %} + + {% endif %} +
+
+ {% endfor %} +
+ {% endif %} + {% if not readonly and not disabled %} +
+
+ {% if allowed_keys is empty %} + + {% else %} + + {% endif %} +
+
+ {% if allowed_values is empty %} + + {% else %} + + {% endif %} +
+
+ {% endif %} +
+
    + {%- block field_errors -%} + {%- for error in errors -%} +
  • {{error}}
  • + {%- endfor -%} + {%- endblock -%} +
+ {% if translations.input_help is defined %} +
{{ translations.input_help|raw }}
+ {% endif %} + {% if translations.input_hint is defined %} +
{{ translations.input_hint|raw }}
+ {% endif %} + {% if translations.input_focus_hint is defined %} +
{{ translations.input_focus_hint|raw }}
+ {% endif %} +
+
+{%- endblock -%} \ No newline at end of file diff --git a/app/templates/html/attribute/key-value-list/as_itemlist_item_cell.twig b/app/templates/html/attribute/key-value-list/as_itemlist_item_cell.twig new file mode 100644 index 00000000..76e2b96b --- /dev/null +++ b/app/templates/html/attribute/key-value-list/as_itemlist_item_cell.twig @@ -0,0 +1,17 @@ +{%- block field -%} {# this block exists for whitespace control #} +
{{ _(field_name~'.field_name', translation_domain) }}
+ {% if attribute_value is iterable %} + {%- for key, value in attribute_value -%} +
{{key}}:
+
{{value}}
+ {%- endfor -%} + {%- else -%} + {{ attribute_value }} + {%- endif -%} +
+{%- endblock field -%} diff --git a/pub/static/themes/whitelabel/_lib.scss b/pub/static/themes/whitelabel/_lib.scss index 27c96943..48a09cca 100644 --- a/pub/static/themes/whitelabel/_lib.scss +++ b/pub/static/themes/whitelabel/_lib.scss @@ -46,6 +46,7 @@ @import "components/primary-activities/components.primary-activities"; @import "components/subheader-activities/components.subheader-activities"; @import "components/itemlist/components.itemlist"; +@import "components/key-value-list/components.key-value-list"; @import "components/image-list/components.image-list"; @import "components/breadcrumb/components.breadcrumb"; @import "components/datepicker/components.datepicker"; diff --git a/pub/static/themes/whitelabel/components/field/_components.field.scss b/pub/static/themes/whitelabel/components/field/_components.field.scss index c080f935..aed07909 100644 --- a/pub/static/themes/whitelabel/components/field/_components.field.scss +++ b/pub/static/themes/whitelabel/components/field/_components.field.scss @@ -1,3 +1,23 @@ .hb-field { margin-bottom: 1.43em; // 20/14 + + // KeyValueList attribute + .hb-field__pair-key, + .hb-field__pair-value { + display: inline-block; + } + + .hb-field__pair-key { + font-style: italic; + } + + .hb-field__pair-value { + &:after { + content: ','; + margin-right: 0.2em; + } + &:last-child { + &:after { content: ''; } + } + } } diff --git a/pub/static/themes/whitelabel/components/key-value-list/_components.key-value-list.scss b/pub/static/themes/whitelabel/components/key-value-list/_components.key-value-list.scss new file mode 100644 index 00000000..8d1fce90 --- /dev/null +++ b/pub/static/themes/whitelabel/components/key-value-list/_components.key-value-list.scss @@ -0,0 +1,42 @@ +.hb-key-value-list { + display: table; + table-layout: fixed; + width: 100%; + + margin-top: $small-spacing; + margin-bottom: $small-spacing; + + @at-root { + .hb-key-value-list__head { + display: table-header-group; + + &.hb-key-value-list__head--hidden { + display: none; + } + } + .hb-key-value-head__label { + display: table-cell; + } + .hb-key-value-list__items { + display: table-row-group; + } + + .hb-key-value-list__item { + display: table-row; + } + + .hb-key-value-item__key, + .hb-key-value-item__value { + display: table-cell; + width: 50%; + } + + .hb-key-value-list__new-item { + display: table-footer-group; + + .hb-key-value-item__key { + padding-top: $small-spacing; + } + } + } +} \ No newline at end of file