From 5471b909e20f4f2a905ebba666f5c55713780af8 Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Sun, 9 Dec 2018 20:11:31 -0800 Subject: [PATCH] Added mechanism for displaying fields conditionally based on tags --- data/presets/README.md | 20 ++++++++ data/presets/fields.json | 6 +-- data/presets/fields/denomination.json | 7 ++- data/presets/fields/internet_access/fee.json | 6 ++- data/presets/fields/internet_access/ssid.json | 6 ++- data/presets/schema/field.json | 22 +++++++++ modules/ui/field.js | 47 +++++++++++++++++-- modules/ui/form_fields.js | 13 ++++- modules/ui/preset_editor.js | 1 + 9 files changed, 114 insertions(+), 14 deletions(-) diff --git a/data/presets/README.md b/data/presets/README.md index cecdbfa5d..fc1abf0b5 100644 --- a/data/presets/README.md +++ b/data/presets/README.md @@ -233,6 +233,26 @@ For number fields, the lowest valid value. There is no default. For number fields, the greatest valid value. There is no default. +##### `prerequisiteTag` + +An object defining the tags the feature needs before this field will be displayed. It must have this property: + +- `key`: The key for the required tag. + +And may optionally have one of these properties: + +- `value`: The value that the key must have. +- `valueNot`: The value that the key must not have. + +For example, this is how we show the Internet Access Field only if the feature has an `internet_access` tag not equal to `no`. + +```js +"prerequisiteTag": { + "key": "internet_access", + "valueNot": "no" +} +``` + ## Icons You can use any of the following open source map icon sets as preset icons. diff --git a/data/presets/fields.json b/data/presets/fields.json index 056f65bd3..5601d7761 100644 --- a/data/presets/fields.json +++ b/data/presets/fields.json @@ -74,7 +74,7 @@ "dance/style": {"key": "dance:style", "type": "semiCombo", "label": "Dance Styles"}, "date": {"key": "date", "type": "check", "label": "Date"}, "delivery": {"key": "delivery", "type": "check", "label": "Delivery"}, - "denomination": {"key": "denomination", "type": "combo", "label": "Denomination"}, + "denomination": {"key": "denomination", "type": "combo", "label": "Denomination", "prerequisiteTag": {"key": "religion"}}, "denotation": {"key": "denotation", "type": "combo", "label": "Denotation"}, "description": {"key": "description", "type": "textarea", "label": "Description", "universal": true}, "design": {"key": "design", "type": "combo", "label": "Design"}, @@ -147,8 +147,8 @@ "inscription": {"key": "inscription", "type": "textarea", "label": "Inscription"}, "intermittent": {"key": "intermittent", "type": "check", "label": "Intermittent"}, "internet_access": {"key": "internet_access", "type": "combo", "label": "Internet Access", "strings": {"options": {"yes": "Yes", "no": "No", "wlan": "Wifi", "wired": "Wired", "terminal": "Terminal"}}}, - "internet_access/fee": {"key": "internet_access:fee", "type": "check", "label": "Internet Access Fee"}, - "internet_access/ssid": {"key": "internet_access:ssid", "type": "text", "label": "SSID (Network Name)"}, + "internet_access/fee": {"key": "internet_access:fee", "type": "check", "label": "Internet Access Fee", "prerequisiteTag": {"key": "internet_access", "valueNot": "no"}}, + "internet_access/ssid": {"key": "internet_access:ssid", "type": "text", "label": "SSID (Network Name)", "prerequisiteTag": {"key": "internet_access", "valueNot": "no"}}, "kerb": {"key": "kerb", "type": "combo", "label": "Curb"}, "label": {"key": "label", "type": "textarea", "label": "Label"}, "lamp_type": {"key": "lamp_type", "type": "combo", "label": "Type"}, diff --git a/data/presets/fields/denomination.json b/data/presets/fields/denomination.json index a4a24ca9d..71d664d98 100644 --- a/data/presets/fields/denomination.json +++ b/data/presets/fields/denomination.json @@ -1,5 +1,8 @@ { "key": "denomination", "type": "combo", - "label": "Denomination" -} \ No newline at end of file + "label": "Denomination", + "prerequisiteTag": { + "key": "religion" + } +} diff --git a/data/presets/fields/internet_access/fee.json b/data/presets/fields/internet_access/fee.json index 9b2e407d4..2ca48b4ba 100644 --- a/data/presets/fields/internet_access/fee.json +++ b/data/presets/fields/internet_access/fee.json @@ -1,5 +1,9 @@ { "key": "internet_access:fee", "type": "check", - "label": "Internet Access Fee" + "label": "Internet Access Fee", + "prerequisiteTag": { + "key": "internet_access", + "valueNot": "no" + } } diff --git a/data/presets/fields/internet_access/ssid.json b/data/presets/fields/internet_access/ssid.json index 5f6b4b56f..2309c2b38 100644 --- a/data/presets/fields/internet_access/ssid.json +++ b/data/presets/fields/internet_access/ssid.json @@ -1,5 +1,9 @@ { "key": "internet_access:ssid", "type": "text", - "label": "SSID (Network Name)" + "label": "SSID (Network Name)", + "prerequisiteTag": { + "key": "internet_access", + "valueNot": "no" + } } diff --git a/data/presets/schema/field.json b/data/presets/schema/field.json index b77a28413..41f8f7de1 100644 --- a/data/presets/schema/field.json +++ b/data/presets/schema/field.json @@ -129,6 +129,28 @@ "maxValue": { "description": "Maximum field value (number fields only)", "type": "integer" + }, + "prerequisiteTag": { + "description": "Tagging constraint for showing this field in the editor", + "type": { + "type": "object", + "properties": { + "key": { + "description": "The key of the required tag", + "type": "string", + "required": true + }, + "value": { + "description": "The value that the tag must have. (alternative to 'valueNot')", + "type": "string" + }, + "valueNot": { + "description": "The value that the tag cannot have. (alternative to 'value')", + "type": "string" + } + }, + "additionalProperties": false + } } }, "additionalProperties": false diff --git a/modules/ui/field.js b/modules/ui/field.js index 86ae3f9d0..b69e5ecd6 100644 --- a/modules/ui/field.js +++ b/modules/ui/field.js @@ -40,10 +40,13 @@ export function uiField(context, presetField, entity, options) { dispatch.call('change', field, t, onInput); }); - // if this field cares about the entity, pass it along - if (entity && field.impl.entity) { + if (entity) { field.entityID = entity.id; - field.impl.entity(entity); + + // if this field cares about the entity, pass it along + if (field.impl.entity) { + field.impl.entity(entity); + } } field.keys = field.keys || [field.key]; @@ -206,6 +209,11 @@ export function uiField(context, presetField, entity, options) { }; + field.hasValue = function() { + return _some(field.keys, function(key) { return !!_tags[key]; }); + }; + + field.show = function() { _show = true; if (field.default && field.key && _tags[field.key] !== field.default) { @@ -215,9 +223,38 @@ export function uiField(context, presetField, entity, options) { } }; - + // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown field.isShown = function() { - return _show || _some(field.keys, function(key) { return !!_tags[key]; }); + return _show || field.hasValue(); + }; + + // An allowed field can appear in the UI or in the 'Add field' dropdown. + // A non-allowed field is hidden from the user altogether + field.isAllowed = function() { + + if (field.hasValue()) { + // always allow a field with a value to display + return true; + } + + var prerequisiteTag = field.prerequisiteTag; + if (prerequisiteTag && field.entityID) { + if (prerequisiteTag.key) { + var value = context.graph().entity(field.entityID).tags[prerequisiteTag.key]; + if (value) { + if (prerequisiteTag.valueNot) { + return prerequisiteTag.valueNot !== value; + } + if (prerequisiteTag.value) { + return prerequisiteTag.value === value; + } + return true; + } else { + return false; + } + } + } + return true; }; diff --git a/modules/ui/form_fields.js b/modules/ui/form_fields.js index 0bf150b22..ece830ac0 100644 --- a/modules/ui/form_fields.js +++ b/modules/ui/form_fields.js @@ -14,9 +14,18 @@ export function uiFormFields(context) { } + formFields.tagsChanged = function() {}; + function render(selection, klass) { - var shown = _fieldsArr.filter(function(field) { return field.isShown(); }); - var notShown = _fieldsArr.filter(function(field) { return !field.isShown(); }); + + formFields.tagsChanged = function() { + render(selection, klass); + }; + + var allowedFields = _fieldsArr.filter(function(field) { return field.isAllowed(); }); + + var shown = allowedFields.filter(function(field) { return field.isShown(); }); + var notShown = allowedFields.filter(function(field) { return !field.isShown(); }); var container = selection.selectAll('.form-fields-container') .data([0]); diff --git a/modules/ui/preset_editor.js b/modules/ui/preset_editor.js index 9ccd9612a..c10f3a2be 100644 --- a/modules/ui/preset_editor.js +++ b/modules/ui/preset_editor.js @@ -117,6 +117,7 @@ export function uiPresetEditor(context) { presetEditor.tags = function(val) { if (!arguments.length) return _tags; _tags = val; + formFields.tagsChanged(); // Don't reset _fieldsArr here. return presetEditor; };