Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

feat: date picker #124

Merged
merged 6 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- [Form](../src/components/Form//README.md)
- [Header](../src/components/Header//README.md)
- [Input](../src/components/Input//README.md)
- [InputDatePicker](../src/components/InputDatePicker//README.md)
- [InputSelect](../src/components/InputSelect//README.md)
- [Nav](../src/components/Nav//README.md)
- [QuickSearchForm](../src/components/QuickSearchForm//README.md)
Expand Down
50 changes: 50 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@semantic-release/release-notes-generator": "^12.1.0",
"@types/lodash": "^4.17.13",
"@types/node": "^20.8.9",
"@typescript-eslint/eslint-plugin": "^6.9.1",
"@typescript-eslint/parser": "^6.9.1",
Expand Down Expand Up @@ -92,6 +93,7 @@
"@fortawesome/vue-fontawesome": "^3.0.8",
"@vuelidate/core": "^2.0.3",
"@vuelidate/validators": "^2.0.4",
"@vuepic/vue-datepicker": "^10.0.0",
"fs-extra": "^11.1.1",
"happy-dom": "^6.0.4",
"minimist": "^1.2.8",
Expand Down
138 changes: 138 additions & 0 deletions src/components/InputDatePicker/PdapInputDatePicker.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<template>
<div class="pdap-input" :class="{ 'pdap-input-error': error }">
<label v-if="$slots.label" :id="`${name}-${id}-label`" :for="id">
<slot name="label" />
</label>
<label v-else-if="label" :id="`${name}-${id}-label`" :for="id">
{{ label }}
</label>
<div v-if="$slots.error && error" class="pdap-input-error-message">
<slot name="error" />
</div>
<div v-else-if="error" class="pdap-input-error-message">{{ error }}</div>

<VueDatePicker
v-bind="{ ...$attrs, ...$props }"
v-model="date"
:state="error"
:dark="darkModePreference"
/>
</div>
</template>

<script setup lang="ts">
import { computed, inject, onMounted, onUnmounted, ref, watch } from 'vue';
import VueDatePicker from '@vuepic/vue-datepicker';
import '@vuepic/vue-datepicker/dist/main.css';
import { provideKey } from '../FormV2/util';
import { PdapFormProvideV2 } from '../FormV2/types';
import { PdapDatePickerProps } from './types';
import _isEqual from 'lodash/isEqual';

const { setValues, values, v$ } = inject<PdapFormProvideV2>(provideKey)!;
const { label, name, id } = defineProps<PdapDatePickerProps>();

const date = ref();
const error = computed(() => v$.value[name]?.$errors?.[0]?.$message);

// TODO: when decision made on whether to do dark them with inputs, pass as `:dark` prop to `VueDatePicker` (also add listener for updates )
const darkModeQuery = ref(window.matchMedia('(prefers-color-scheme: dark)'));
const darkModePreference = ref(darkModeQuery.value.matches);

function updateColorMode(e: MediaQueryListEvent) {
darkModePreference.value = e.matches;
}
onMounted(() => {
darkModeQuery.value.addEventListener('change', updateColorMode);
});
onUnmounted(() => {
darkModeQuery.value.removeEventListener('change', updateColorMode);
});

watch(
() => date.value,
(newDate) => {
// Sync values with underlying input, for form
if (newDate) setValues({ [name]: newDate });
}
);

watch(
// Welcome to the land of edge-cases.
() => values.value,
// In the (unlikely, unrecommended, but sometimes unfortunately necessary) event of form values changing upstream from a parent component:
(formValuesUpdated) => {
// Case 0: Values are equivalent, or the change was made here, do nothing.

/*
* Case 1: Value does not exist in form values object, meaning either:
** a. it has not been set by the upstream change, or
** b. has been changed to an empty string by `Form` after submit event
** In either case, clear state or keep it clear
*/
if (!formValuesUpdated[name]) date.value = undefined;
// Case 2 (rare): value has been programmatically updated upstream of `Form`
else if (
!_isEqual(formValuesUpdated[name], date.value) &&
formValuesUpdated[name] instanceof Date
) {
// Set the date to the value of the form value
date.value = formValuesUpdated[name];
}
}
);
</script>

<style>
@tailwind base;

.dp__theme_light,
.dp__theme_dark {
/* FULL LIST OF CUSTOMIZING OPTIONS from https://vue3datepicker.com/customization/theming/ */
/* --dp-background-color: #212121;
--dp-text-color: #fff;
--dp-hover-color: #484848;
--dp-hover-text-color: #fff;
--dp-hover-icon-color: #959595;
--dp-primary-color: #005cb2;
--dp-primary-disabled-color: #61a8ea;
--dp-primary-text-color: #fff;
--dp-secondary-color: #a9a9a9;
--dp-border-color: #2d2d2d;
--dp-menu-border-color: #2d2d2d;
--dp-border-color-hover: #aaaeb7;
--dp-border-color-focus: #aaaeb7;
--dp-disabled-color: #737373;
--dp-disabled-color-text: #d0d0d0;
--dp-scroll-bar-background: #212121;
--dp-scroll-bar-color: #484848;
--dp-success-color: #00701a;
--dp-success-color-disabled: #428f59;
--dp-icon-color: #959595;
--dp-danger-color: #e53935;
--dp-marker-color: #e53935;
--dp-tooltip-color: #3e3e3e;
--dp-highlight-color: rgb(0 92 178 / 20%);
--dp-range-between-dates-background-color: var(--dp-hover-color, #484848);
--dp-range-between-dates-text-color: var(--dp-hover-text-color, #fff);
--dp-range-between-border-color: var(--dp-hover-color, #fff); */
}

.dp__theme_light {
--dp-primary-color: rgb(var(--color-wine-neutral-700));
--dp-primary-text-color: rgb(var(--color-neutral-50));
}

.dp__theme_dark {
--dp-primary-color: rgb(var(--color-wine-neutral-300));
--dp-primary-text-color: rgb(var(--color-neutral-950));
}

.pdap-input .dp__input {
@apply text-lg pl-9;
}

div[role='gridcell'] {
@apply outline-none border-2 border-solid border-transparent;
}
</style>
53 changes: 53 additions & 0 deletions src/components/InputDatePicker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# InputSelect
Date picker component. Uses Vue3 Date Picker library under the hood.

## Props - required

| name | required? | types | description | default |
| ------- | ----------------------------- | -------- | ------------- | ------- |
| `id` | yes | `string` | id attr | |
| `label` | yes, if label slot not passed | `string` | label content | |
| `name` | yes | `string` | name attr | |

## Props - Vue3 Date Picker
The props interface extends the underlying component interface, so [all props available on the Vue 3 Date Picker component](https://vue3datepicker.com/props/modes/) are available to be passed.

## Slots

| name | required? | types | description | default |
| ------- | ----------------------------- | --------- | ------------------------------------ | ------- |
| `error` | no<sup>*</sup> | `Element` | slot content to be rendered as error | |
| `label` | yes, if label prop not passed | `Element` | slot content to be rendered as label | |

<sup>*</sup> Note: The error message is determined by Vuelidate via our form validation schema. If the error UI needs to be more complicated than a string that can be passed with the schema, pass an `\#error` slot and it will override the string.

## Example

```vue
<template>
<FormV2
id="form-id"
name="ice-cream-preference"
:schema="SCHEMA"
@submit="(values) => onSubmit({ values })"
@change="(values, event) => onChange({ values, event })"
>
<!-- Other inputs... -->
<InputDatePicker
:id="INPUT_DATE_NAME"
:name="INPUT_DATE_NAME"
position="left"
>
<template #label>
<h4>When will you next consume ice cream?</h4>
</template>
</InputDatePicker>
</FormV2>
</template>

<script setup>
import { InputDatePicker, FormV2 } from 'pdap-design-system';
</script>

...
```
Loading
Loading