From 536ce81889c1b62ac5acc9861bea0edea710f4b6 Mon Sep 17 00:00:00 2001 From: BeneRichi Date: Fri, 5 Jan 2024 16:01:06 +0100 Subject: [PATCH 1/4] 321: Implement e-date and c-date-picker components and integrates it into r-forms. - Adds pikaday dependency and type definition. --- package-lock.json | 60 ++- package.json | 2 + src/assets/icons.svg | 2 +- src/assets/icons/i-calendar.svg | 4 + src/components/c-date-picker.vue | 644 +++++++++++++++++++++++++ src/compositions/form-states.ts | 16 +- src/elements/e-date.vue | 178 +++++++ src/plugins/dayjs.ts | 11 + src/setup/scss/variables/_font.scss | 6 + src/setup/scss/variables/_z-index.scss | 1 + src/styleguide/routes/r-forms.vue | 40 +- src/translations/de.json | 9 + 12 files changed, 953 insertions(+), 20 deletions(-) create mode 100644 src/assets/icons/i-calendar.svg create mode 100644 src/components/c-date-picker.vue create mode 100644 src/elements/e-date.vue diff --git a/package-lock.json b/package-lock.json index 182e1786..0b540b1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "dayjs": "~1.11.7", "embla-carousel": "~7.0.9", "embla-carousel-autoplay": "~7.0.9", + "pikaday": "~1.8.2", "pinia": "~2.0.30", "the-new-css-reset": "~1.8.4", "vue": "~3.2.47", @@ -35,6 +36,7 @@ "@types/body-scroll-lock": "~3.1.0", "@types/google.maps": "~3.51.0", "@types/node": "~18.13.0", + "@types/pikaday": "~1.7.6", "@types/resize-observer-browser": "~0.1.6", "@typescript-eslint/eslint-plugin": "~5.52.0", "@typescript-eslint/parser": "~5.52.0", @@ -8251,6 +8253,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/pikaday": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/@types/pikaday/-/pikaday-1.7.9.tgz", + "integrity": "sha512-DZ6oG5WoYX9Es2VTgvJpzxKMTU4WE0jq9noVSs0VTdmCREYv/AXSaybQj0Sm9Q9XO1pmFrdfBCpL4OVRX/QkAg==", + "dev": true, + "dependencies": { + "moment": ">=2.29.2" + } + }, "node_modules/@types/pretty-hrtime": { "version": "1.0.1", "dev": true, @@ -10952,7 +10963,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001429", + "version": "1.0.30001574", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001574.tgz", + "integrity": "sha512-BtYEK4r/iHt/txm81KBudCUcTy7t+s9emrIaHqjYurQ10x71zJ5VQ9x1dYPcz/b+pKSp4y/v1xSI67A+LzpNyg==", "dev": true, "funding": [ { @@ -10962,9 +10975,12 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/capture-exit": { "version": "2.0.0", @@ -19694,6 +19710,15 @@ "ufo": "^1.0.1" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/move-concurrently": { "version": "1.0.1", "dev": true, @@ -21025,6 +21050,11 @@ "node": ">=6" } }, + "node_modules/pikaday": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/pikaday/-/pikaday-1.8.2.tgz", + "integrity": "sha512-TNtsE+34BIax3WtkB/qqu5uepV1McKYEgvL3kWzU7aqPCpMEN6rBF3AOwu4WCwAealWlBGobXny/9kJb49C1ew==" + }, "node_modules/pinia": { "version": "2.0.30", "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.30.tgz", @@ -34274,6 +34304,15 @@ "version": "5.0.3", "dev": true }, + "@types/pikaday": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/@types/pikaday/-/pikaday-1.7.9.tgz", + "integrity": "sha512-DZ6oG5WoYX9Es2VTgvJpzxKMTU4WE0jq9noVSs0VTdmCREYv/AXSaybQj0Sm9Q9XO1pmFrdfBCpL4OVRX/QkAg==", + "dev": true, + "requires": { + "moment": ">=2.29.2" + } + }, "@types/pretty-hrtime": { "version": "1.0.1", "dev": true @@ -36246,7 +36285,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001429", + "version": "1.0.30001574", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001574.tgz", + "integrity": "sha512-BtYEK4r/iHt/txm81KBudCUcTy7t+s9emrIaHqjYurQ10x71zJ5VQ9x1dYPcz/b+pKSp4y/v1xSI67A+LzpNyg==", "dev": true }, "capture-exit": { @@ -42348,6 +42389,12 @@ "ufo": "^1.0.1" } }, + "moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "dev": true + }, "move-concurrently": { "version": "1.0.1", "dev": true, @@ -43281,6 +43328,11 @@ "version": "4.0.1", "dev": true }, + "pikaday": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/pikaday/-/pikaday-1.8.2.tgz", + "integrity": "sha512-TNtsE+34BIax3WtkB/qqu5uepV1McKYEgvL3kWzU7aqPCpMEN6rBF3AOwu4WCwAealWlBGobXny/9kJb49C1ew==" + }, "pinia": { "version": "2.0.30", "resolved": "https://registry.npmjs.org/pinia/-/pinia-2.0.30.tgz", diff --git a/package.json b/package.json index 8ec4730c..9807c3ca 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "dayjs": "~1.11.7", "embla-carousel": "~7.0.9", "embla-carousel-autoplay": "~7.0.9", + "pikaday": "~1.8.2", "pinia": "~2.0.30", "the-new-css-reset": "~1.8.4", "vue": "~3.2.47", @@ -87,6 +88,7 @@ "@types/body-scroll-lock": "~3.1.0", "@types/google.maps": "~3.51.0", "@types/node": "~18.13.0", + "@types/pikaday": "~1.7.6", "@types/resize-observer-browser": "~0.1.6", "@typescript-eslint/eslint-plugin": "~5.52.0", "@typescript-eslint/parser": "~5.52.0", diff --git a/src/assets/icons.svg b/src/assets/icons.svg index 95ed0f1f..9232e4b1 100644 --- a/src/assets/icons.svg +++ b/src/assets/icons.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/assets/icons/i-calendar.svg b/src/assets/icons/i-calendar.svg new file mode 100644 index 00000000..50a1b5f3 --- /dev/null +++ b/src/assets/icons/i-calendar.svg @@ -0,0 +1,4 @@ + + + diff --git a/src/components/c-date-picker.vue b/src/components/c-date-picker.vue new file mode 100644 index 00000000..46669453 --- /dev/null +++ b/src/components/c-date-picker.vue @@ -0,0 +1,644 @@ + + + + + diff --git a/src/compositions/form-states.ts b/src/compositions/form-states.ts index 938a93bb..ef44ffcd 100644 --- a/src/compositions/form-states.ts +++ b/src/compositions/form-states.ts @@ -6,7 +6,7 @@ import { ref, } from 'vue'; -export enum FieldStates { +export enum FieldState { Default = 'default', Success = 'success', Info = 'info', @@ -15,7 +15,7 @@ export enum FieldStates { } interface StateModifiers { - state: FieldStates; + state: FieldState; active: boolean; focus: boolean; hover: boolean; @@ -35,7 +35,7 @@ export const withProps = () => ({ // eslint-disable-line -- TODO: did not know h * Form states for class names (default, error, success, warning, info) */ state: { - type: String as PropType, + type: String as PropType, default: 'default', validator: (value: string): boolean => [ 'error', @@ -50,7 +50,7 @@ export const withProps = () => ({ // eslint-disable-line -- TODO: did not know h /** * Defines the reactive properties which can be used for form elements */ -const formStates = (inputState: Ref): FormStates => { +const formStates = (inputState: Ref): FormStates => { const active = ref(false); const focus = ref(false); const hover = ref(false); @@ -68,20 +68,20 @@ const formStates = (inputState: Ref): FormStates => { /** * Holds a boolean if the form element has default state. */ - const hasDefaultState: ComputedRef = computed(() => inputState.value === FieldStates.Default); + const hasDefaultState: ComputedRef = computed(() => inputState.value === FieldState.Default); /** * Holds a string containing the icon name matching the current form element state. */ const stateIcon: ComputedRef = computed(() => { switch (inputState.value) { - case FieldStates.Error: + case FieldState.Error: return 'i-error'; - case FieldStates.Success: + case FieldState.Success: return 'i-check'; - case FieldStates.Info: + case FieldState.Info: return 'i-info'; default: diff --git a/src/elements/e-date.vue b/src/elements/e-date.vue new file mode 100644 index 00000000..41bb6348 --- /dev/null +++ b/src/elements/e-date.vue @@ -0,0 +1,178 @@ + + + + + diff --git a/src/plugins/dayjs.ts b/src/plugins/dayjs.ts index 470b6bfc..e1c2e452 100644 --- a/src/plugins/dayjs.ts +++ b/src/plugins/dayjs.ts @@ -7,6 +7,17 @@ import isBetween from 'dayjs/plugin/isBetween'; import { Plugin } from 'vue'; import de from 'dayjs/locale/de'; +/** + * Holds the date format templates used with dayjs. + */ +/* eslint-disable @typescript-eslint/naming-convention */ +export enum DateFormat { + DD_MM_YYYY = 'DD.MM.YYYY', + DD_MM_YYYY_HH_mm = 'DD.MM.YYYY HH:mm', + dddd_DD_MMMM_YYYY = 'dddd [/] DD. MMMM YYYY', + dddd_DD_MMMM_YYYY_HH_mm = 'dddd [/] DD. MMMM YYYY HH:mm' +} + const plugin: Plugin = { install(app) { dayjs.locale('de-ch', de); diff --git a/src/setup/scss/variables/_font.scss b/src/setup/scss/variables/_font.scss index 17d0ea50..7c76580d 100644 --- a/src/setup/scss/variables/_font.scss +++ b/src/setup/scss/variables/_font.scss @@ -14,3 +14,9 @@ $font-size--16: 16px; $font-size--18: 18px; $font-size--24: 24px; $font-size--30: 30px; + +// Line height +$line-height--18: 18px; +$line-height--20: 20px; +$line-height--25: 25px; +$line-height--30: 30px; diff --git a/src/setup/scss/variables/_z-index.scss b/src/setup/scss/variables/_z-index.scss index 45b82001..ce1e4e53 100644 --- a/src/setup/scss/variables/_z-index.scss +++ b/src/setup/scss/variables/_z-index.scss @@ -3,6 +3,7 @@ $z-index: ( front: 1, navigation: 10, dropdown: 15, + datePicker: 700, focusItem: 30, globalNotification: 900, modal: 1000, diff --git a/src/styleguide/routes/r-forms.vue b/src/styleguide/routes/r-forms.vue index c25d1202..64179cae 100644 --- a/src/styleguide/routes/r-forms.vue +++ b/src/styleguide/routes/r-forms.vue @@ -27,7 +27,7 @@ + + + + + + + + + + + + Submit @@ -176,7 +192,9 @@ import eCheckbox from '@/elements/e-checkbox.vue'; import eTextarea from '@/elements/e-textarea.vue'; import eButton from '@/elements/e-button.vue'; - import { FieldStates } from '@/compositions/form-states'; + import { FieldState } from '@/compositions/form-states'; + import eDate from '@/elements/e-date.vue'; + import cDatePicker from '@/components/c-date-picker.vue'; interface SelectItem { label: string; @@ -186,7 +204,7 @@ interface Setup { v$: Ref; formRef: Ref; - FieldStates: typeof FieldStates; + FieldState: typeof FieldState; } interface Data { @@ -200,6 +218,9 @@ topics: string[]; frequency: string; businessFields: string[]; + date: Date; + startDate: Date; + endDate: Date; }; mock: { businessFields: SelectItem[]; @@ -221,6 +242,8 @@ eCheckbox, eTextarea, eButton, + cDatePicker, + eDate, }, setup(): Setup { @@ -230,7 +253,7 @@ // eslint-disable-next-line id-length v$: useVuelidate(), formRef, - FieldStates, + FieldState, }; }, @@ -246,6 +269,9 @@ topics: ['technics'], frequency: 'twiceAMonth', businessFields: [], + date: new Date(), + startDate: new Date(), + endDate: new Date(), }, mock: { languages: [ diff --git a/src/translations/de.json b/src/translations/de.json index 7e2ae50e..1097ccc9 100644 --- a/src/translations/de.json +++ b/src/translations/de.json @@ -45,6 +45,15 @@ "itemsEmpty": "Keine Ergebnisse.", "searchTitle": "Suche" }, + "c-date-picker": { + "calendarRangeTitle": "Datumsbereich auswählen", + "calendarTitle": "Datum auswählen", + "inputEndLabel": "Ende", + "inputLabel": "Datum", + "inputStartLabel": "Start", + "nextMonth": "Nächster Monat", + "previousMonth": "Vorheriger Monat" + }, "c-slider":{ "navigationPrevious": "Zurück", "navigationNext": "Vorwärts" From fe797eaa1497c6154027f01c78b5da43a4cf31bd Mon Sep 17 00:00:00 2001 From: BeneRichi Date: Thu, 11 Jan 2024 14:05:22 +0100 Subject: [PATCH 2/4] 321: Removes linting comments. - Re-Orders z-index variables - Changes root div element to span in e-date - Adds nextThick in mounted hook in c-date-picker --- src/components/c-date-picker.vue | 6 ++++-- src/elements/e-date.vue | 8 ++++---- src/plugins/dayjs.ts | 2 -- src/setup/scss/variables/_z-index.scss | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/c-date-picker.vue b/src/components/c-date-picker.vue index 46669453..e5350da4 100644 --- a/src/components/c-date-picker.vue +++ b/src/components/c-date-picker.vue @@ -248,7 +248,9 @@ // beforeMount() {}, mounted() { if (this.isCalendarVisible) { - this.createCalendar(); + this.$nextTick(() => { + this.createCalendar(); + }); } }, // beforeUpdate() {}, @@ -367,7 +369,7 @@ if (this.$el === activeElement || this.$el.contains(activeElement)) { // Removes focus on child elements so datepicker can be opened instantly again. - activeElement.blur(); // eslint-disable-line no-extra-parens + activeElement.blur(); } this.isCalendarVisible = false; diff --git a/src/elements/e-date.vue b/src/elements/e-date.vue index 41bb6348..9c6050f1 100644 --- a/src/elements/e-date.vue +++ b/src/elements/e-date.vue @@ -1,5 +1,5 @@