diff --git a/css/80_app.css b/css/80_app.css index c2d0944b8..738d3a268 100644 --- a/css/80_app.css +++ b/css/80_app.css @@ -1536,7 +1536,11 @@ a.hide-toggle { border-color: #f1f1f1; } } -.form-field-button.colour-selector { +input.colour-selector { + visibility: hidden; + position: absolute; +} +input.date-selector { visibility: hidden; position: absolute; } diff --git a/data/core.yaml b/data/core.yaml index c2706f78b..ebe5892e4 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -784,6 +784,7 @@ en: # abbreviation of inches inch: in max_length_reached: "This string is longer than the maximum length of {maxChars} characters. Anything exceeding that length will be truncated." + set_today: "Sets the value to today." background: title: Background description: Background Settings diff --git a/modules/ui/fields/index.js b/modules/ui/fields/index.js index 73733044b..ba5cb0c34 100644 --- a/modules/ui/fields/index.js +++ b/modules/ui/fields/index.js @@ -63,6 +63,7 @@ export var uiFields = { colour: uiFieldColour, combo: uiFieldCombo, cycleway: uiFieldDirectionalCombo, + date: uiFieldText, defaultCheck: uiFieldDefaultCheck, directionalCombo: uiFieldDirectionalCombo, email: uiFieldEmail, diff --git a/modules/ui/fields/input.js b/modules/ui/fields/input.js index 92de39beb..028ba46cb 100644 --- a/modules/ui/fields/input.js +++ b/modules/ui/fields/input.js @@ -6,10 +6,11 @@ import * as countryCoder from '@ideditor/country-coder'; import { presetManager } from '../../presets'; import { fileFetcher } from '../../core/file_fetcher'; import { t, localizer } from '../../core/localizer'; -import { utilGetSetValue, utilNoAuto, utilRebind, utilTotalExtent } from '../../util'; +import { utilDetect, utilGetSetValue, utilNoAuto, utilRebind, utilTotalExtent } from '../../util'; import { svgIcon } from '../../svg/icon'; import { cardinal } from '../../osm/node'; import { uiLengthIndicator } from '..'; +import { uiTooltip } from '../tooltip'; export { uiFieldText as uiFieldColour, @@ -208,38 +209,44 @@ export function uiFieldText(field, context) { input.attr('type', 'text'); updateColourPreview(); + } else if (field.type === 'date') { + input.attr('type', 'text'); + + updateDateField(); } } - function isColourValid(colour) { - if (!colour.match(/^(#([0-9a-fA-F]{3}){1,2}|\w+)$/)) { - // OSM only supports hex or named colors - return false; - } else if (!CSS.supports('color', colour) || ['unset', 'inherit', 'initial', 'revert'].includes(colour)) { - // see https://stackoverflow.com/a/68217760/1627467 - return false; - } - return true; - } + function updateColourPreview() { + function isColourValid(colour) { + if (!colour.match(/^(#([0-9a-fA-F]{3}){1,2}|\w+)$/)) { + // OSM only supports hex or named colors + return false; + } else if (!CSS.supports('color', colour) || ['unset', 'inherit', 'initial', 'revert'].includes(colour)) { + // see https://stackoverflow.com/a/68217760/1627467 + return false; + } + return true; + } wrap.selectAll('.colour-preview') .remove(); const colour = utilGetSetValue(input); - if (!isColourValid(colour) && colour !== '') return; + if (!isColourValid(colour) && colour !== '') { + wrap.selectAll('input.colour-selector').remove(); + wrap.selectAll('.form-field-button').remove(); + return; + } var colourSelector = wrap.selectAll('.colour-selector') .data([0]); - outlinkButton = wrap.selectAll('.colour-preview') - .data([colour]); colourSelector .enter() .append('input') .attr('type', 'color') - .attr('class', 'form-field-button colour-selector') - .attr('value', colour) + .attr('class', 'colour-selector') .on('input', _debounce(function(d3_event) { d3_event.preventDefault(); var colour = this.value; @@ -248,8 +255,12 @@ export function uiFieldText(field, context) { change()(); updateColourPreview(); }, 100)); + wrap.selectAll('input.colour-selector') + .attr('value', colour); - outlinkButton = outlinkButton + var chooserButton = wrap.selectAll('.colour-preview') + .data([colour]); + chooserButton = chooserButton .enter() .append('div') .attr('class', 'form-field-button colour-preview') @@ -257,12 +268,83 @@ export function uiFieldText(field, context) { .style('background-color', d => d) .attr('class', 'colour-box'); if (colour === '') { - outlinkButton = outlinkButton + chooserButton = chooserButton .call(svgIcon('#iD-icon-edit')); } - outlinkButton - .on('click', () => wrap.select('.colour-selector').node().click()) - .merge(outlinkButton); + chooserButton + .on('click', () => wrap.select('.colour-selector').node().showPicker()); + } + + + function updateDateField() { + function isDateValid(date) { + return date.match(/^[0-9]{4}(-[0-9]{2}(-[0-9]{2})?)?$/); + } + wrap.selectAll('.date-preview') // todo: rename + .remove(); + + const date = utilGetSetValue(input); + + const now = new Date(); + const today = new Date(now.getTime() - now.getTimezoneOffset() * 60000).toISOString().split('T')[0]; + if (field.key === 'check_date' && date !== today) { + wrap.selectAll('.date-set-today') + .data([0]) + .enter() + .append('button') + .attr('class', 'form-field-button date-set-today') + .call(svgIcon('#fas-rotate')) + .call(uiTooltip().title(() => t.append('inspector.set_today'))) + .on('click', () => { + utilGetSetValue(input, today); + change()(); + updateDateField(); + }); + } else { + wrap.selectAll('.date-set-today').remove(); + } + + if (!isDateValid(date) && date !== '') { + wrap.selectAll('input.date-selector').remove(); + wrap.selectAll('.date-calendar').remove(); + return; + } + + if (utilDetect().browser !== 'Safari') { + console.log(utilDetect()); + // opening of the calendar pick is not yet supported in safari <= 16 + // https://caniuse.com/mdn-api_htmlinputelement_showpicker_date_input + + var dateSelector = wrap.selectAll('.date-selector') + .data([0]); + + dateSelector + .enter() + .append('input') + .attr('type', 'date') + .attr('class', 'date-selector') + .on('input', _debounce(function(d3_event) { + d3_event.preventDefault(); + var date = this.value; + if (!isDateValid(date)) return; + utilGetSetValue(input, this.value); + change()(); + updateDateField(); + }, 100)); + wrap.selectAll('input.date-selector') + .attr('value', date); + + var calendarButton = wrap.selectAll('.date-calendar') + .data([date]); + calendarButton = calendarButton + .enter() + .append('button') + .attr('class', 'form-field-button date-calendar') + .call(svgIcon('#fas-calendar-days')); + + calendarButton + .on('click', () => wrap.select('.date-selector').node().showPicker()); + } } @@ -362,7 +444,9 @@ export function uiFieldText(field, context) { if (field.type === 'tel') updatePhonePlaceholder(); - if (field.key.split(':').includes('colour')) updateColourPreview(); + if (field.type === 'colour') updateColourPreview(); + + if (field.type === 'date') updateDateField(); if (outlinkButton && !outlinkButton.empty()) { var disabled = !validIdentifierValueForLink(); diff --git a/scripts/build_data.js b/scripts/build_data.js index 3d794fe4d..cf1f50b10 100644 --- a/scripts/build_data.js +++ b/scripts/build_data.js @@ -64,7 +64,9 @@ function buildData() { 'fas-i-cursor', 'fas-lock', 'fas-th-list', - 'fas-user-cog' + 'fas-user-cog', + 'fas-calendar-days', + 'fas-rotate' ]); // add icons for QA integrations readQAIssueIcons(faIcons); diff --git a/svg/fontawesome/fas-calendar-days.svg b/svg/fontawesome/fas-calendar-days.svg new file mode 100644 index 000000000..e9183c509 --- /dev/null +++ b/svg/fontawesome/fas-calendar-days.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/svg/fontawesome/fas-rotate.svg b/svg/fontawesome/fas-rotate.svg new file mode 100644 index 000000000..f3712bfbe --- /dev/null +++ b/svg/fontawesome/fas-rotate.svg @@ -0,0 +1 @@ + \ No newline at end of file