@geckou/ui-vue 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +253 -0
  3. package/dist/assets/scss/functions.scss +6 -0
  4. package/dist/assets/scss/mixin.scss +33 -0
  5. package/dist/components/ArticleList/Card/Artistic.vue +310 -0
  6. package/dist/components/ArticleList/Card/Entertainment.vue +244 -0
  7. package/dist/components/ArticleList/Card/Gallery.vue +254 -0
  8. package/dist/components/ArticleList/Card/Grid.vue +247 -0
  9. package/dist/components/ArticleList/Card/News.vue +150 -0
  10. package/dist/components/ArticleList/Card/Rounded.vue +154 -0
  11. package/dist/components/ArticleList/Card/Row.vue +167 -0
  12. package/dist/components/ArticleList/Card/Simple.vue +232 -0
  13. package/dist/components/ArticleList/Card/Standard.vue +187 -0
  14. package/dist/components/ArticleList/Card/Tile.vue +171 -0
  15. package/dist/components/ArticleList/GenericArticleList.vue +143 -0
  16. package/dist/components/ArticleList/List/Artistic.vue +24 -0
  17. package/dist/components/ArticleList/List/Entertainment.vue +24 -0
  18. package/dist/components/ArticleList/List/Gallery.vue +24 -0
  19. package/dist/components/ArticleList/List/Grid.vue +216 -0
  20. package/dist/components/ArticleList/List/News.vue +24 -0
  21. package/dist/components/ArticleList/List/Rounded.vue +24 -0
  22. package/dist/components/ArticleList/List/Row.vue +24 -0
  23. package/dist/components/ArticleList/List/Simple.vue +24 -0
  24. package/dist/components/ArticleList/List/Standard.vue +24 -0
  25. package/dist/components/ArticleList/List/Tile.vue +24 -0
  26. package/dist/components/ArticleList/Parts/AuthorInfo.vue +100 -0
  27. package/dist/components/ArticleList/Parts/CardContainer.vue +23 -0
  28. package/dist/components/ArticleList/Parts/CardHeading.vue +43 -0
  29. package/dist/components/ArticleList/Parts/CategoryList.vue +41 -0
  30. package/dist/components/ArticleList/Parts/ExcerptText.vue +43 -0
  31. package/dist/components/ArticleList/Parts/MetadataList.vue +120 -0
  32. package/dist/components/ArticleList/Parts/NoImage.vue +36 -0
  33. package/dist/components/ArticleList/Parts/PostedDate.vue +41 -0
  34. package/dist/components/ArticleList/Parts/TagList.vue +41 -0
  35. package/dist/components/ArticleList/Parts/ThumbnailImage.vue +85 -0
  36. package/dist/components/BasicButton.vue +143 -0
  37. package/dist/components/CheckBox.vue +140 -0
  38. package/dist/components/CheckBoxes.vue +100 -0
  39. package/dist/components/CheckButton.vue +127 -0
  40. package/dist/components/DatePicker.vue +234 -0
  41. package/dist/components/DateRangePicker.vue +85 -0
  42. package/dist/components/DateSelector.vue +273 -0
  43. package/dist/components/DropdownUi.vue +118 -0
  44. package/dist/components/ErrorMessage.vue +61 -0
  45. package/dist/components/Icon/CalendarIcon.vue +8 -0
  46. package/dist/components/Icon/CheckIcon.vue +8 -0
  47. package/dist/components/Icon/CloseIcon.vue +8 -0
  48. package/dist/components/Icon/Folder.vue +8 -0
  49. package/dist/components/Icon/Image.vue +14 -0
  50. package/dist/components/Icon/KeyboardArrowDownIcon.vue +8 -0
  51. package/dist/components/Icon/Tag.vue +8 -0
  52. package/dist/components/InputBox.vue +134 -0
  53. package/dist/components/InputGroup.vue +41 -0
  54. package/dist/components/LabeledCheckbox.vue +82 -0
  55. package/dist/components/LabeledFieldset.vue +41 -0
  56. package/dist/components/LoadingSpinner.vue +56 -0
  57. package/dist/components/ModalBox.vue +192 -0
  58. package/dist/components/PopupBox.vue +93 -0
  59. package/dist/components/RadioButtons.vue +163 -0
  60. package/dist/components/SelectBox.vue +166 -0
  61. package/dist/components/SlideDownUi.vue +123 -0
  62. package/dist/components/TabUI.vue +123 -0
  63. package/dist/components/TextArea.vue +115 -0
  64. package/dist/components/TextBox.vue +111 -0
  65. package/dist/components/TextButton.vue +47 -0
  66. package/dist/components/ToggleButton.vue +179 -0
  67. package/dist/const/index.ts +2 -0
  68. package/dist/const/list-theme.ts +62 -0
  69. package/dist/index.ts +126 -0
  70. package/dist/scripts/form-validation-manager.ts +63 -0
  71. package/dist/scripts/utils.ts +36 -0
  72. package/dist/types/custom.d.ts +1 -0
  73. package/dist/types/index.d.ts +93 -0
  74. package/dist/types/vue-shim.d.ts +5 -0
  75. package/package.json +44 -0
@@ -0,0 +1,140 @@
1
+ <script setup lang="ts">
2
+ import type { CheckBoxStyleForEachStatus } from '../types'
3
+ import { computed } from 'vue'
4
+ import CheckIcon from '../components/Icon/CheckIcon.vue'
5
+ import { COLOR } from '../const'
6
+
7
+ const emit = defineEmits<{ (e: 'update:modelValue', newValue: boolean): void }>()
8
+
9
+ const props = withDefaults(defineProps<{
10
+ name: string
11
+ modelValue?: boolean
12
+ isDisabled?: boolean
13
+ cssStyle?: CheckBoxStyleForEachStatus
14
+ isDisableAnimation?: boolean
15
+ }>(), {
16
+ cssStyle: undefined,
17
+ })
18
+
19
+ const isChecked = computed<boolean>({
20
+ get: () => props.modelValue ?? false,
21
+ set: (newValue: boolean) => emit('update:modelValue', newValue),
22
+ })
23
+
24
+ const currentCssStyle = computed(() => {
25
+ const cssStyle = props.isDisabled ? props.cssStyle?.disabled : props.cssStyle?.default
26
+
27
+ return {
28
+ ...{
29
+ textColor : props.isDisabled ? COLOR.darkGray : COLOR.blue,
30
+ backgroundColor: props.isDisabled ? COLOR.lightGray : COLOR.white,
31
+ border : {
32
+ color : props.isDisabled ? COLOR.darkGray : COLOR.blue,
33
+ size : '1px',
34
+ radius: '.25rem',
35
+ },
36
+ },
37
+ ...cssStyle,
38
+ }
39
+ })
40
+ </script>
41
+
42
+ <template>
43
+ <button
44
+ :class="$style.check_box"
45
+ :style="{
46
+ '--text-color': currentCssStyle?.textColor,
47
+ '--border-color': currentCssStyle?.border?.color,
48
+ '--border-size' : currentCssStyle?.border?.size,
49
+ '--radius-size': currentCssStyle?.border?.radius,
50
+ '--background-color': currentCssStyle?.backgroundColor,
51
+ '--duration': isDisableAnimation ? '0s' : '.3s',
52
+ }"
53
+ type="button"
54
+ @click.stop="!isDisabled ? isChecked = !isChecked : null"
55
+ >
56
+ <input
57
+ v-model="isChecked"
58
+ type="checkbox"
59
+ :name="name"
60
+ :disabled="isDisabled"
61
+ >
62
+ <div :class="$style.check_container">
63
+ <slot name="check" />
64
+ <CheckIcon v-if="!$slots.check" />
65
+ </div>
66
+ </button>
67
+ </template>
68
+
69
+ <style lang="scss" module>
70
+ @keyframes pop {
71
+ 0% {
72
+ scale : 1;
73
+ }
74
+
75
+ 10% {
76
+ scale : .8;
77
+ }
78
+
79
+ 50% {
80
+ scale : 1.1;
81
+ }
82
+
83
+ 100% {
84
+ scale: 1;
85
+ }
86
+ }
87
+
88
+ :is(.check_box) {
89
+ display: flex;
90
+ align-items: center;
91
+ justify-content: center;
92
+ width: 1.5rem;
93
+ height: 1.5rem;
94
+ border: none;
95
+ box-shadow: 0 0 0 var(--border-size) var(--border-color) inset;
96
+ border-radius: var(--radius-size);
97
+ background-color: var(--background-color);
98
+ cursor: pointer;
99
+
100
+ > input {
101
+ display: none;
102
+ }
103
+
104
+ &:has(input:disabled) {
105
+ pointer-events: none !important;
106
+ position: relative;
107
+
108
+ &::before {
109
+ content: '';
110
+ position: absolute;
111
+ inset: 0;
112
+ pointer-events: all;
113
+ cursor: not-allowed !important;
114
+ }
115
+ }
116
+
117
+ &:has(input:checked) {
118
+ background-color: var(--border-color);
119
+ animation: pop var(--duration) ease-out;
120
+
121
+ :is(.check_container) {
122
+ color: var(--background-color);
123
+ }
124
+ }
125
+ }
126
+
127
+ :is(.check_container) {
128
+ display: flex;
129
+ align-items: center;
130
+ justify-content: center;
131
+ width: 1rem;
132
+ height: 1rem;
133
+ color: var(--border-color);
134
+
135
+ > * {
136
+ color: currentColor;
137
+ fill: currentColor;
138
+ }
139
+ }
140
+ </style>
@@ -0,0 +1,100 @@
1
+ <script setup lang="ts">
2
+ import type {
3
+ Option,
4
+ CheckBoxStyleForEachStatus,
5
+ } from '../types'
6
+ import {
7
+ computed,
8
+ ref,
9
+ watch,
10
+ } from 'vue'
11
+ import LabeledCheckbox from '../components/LabeledCheckbox.vue'
12
+ import ErrorMessage from '../components/ErrorMessage.vue'
13
+ import { COLOR } from '../const'
14
+
15
+ const emit = defineEmits<{ (e: 'update:modelValue', newValue: string[]): void }>()
16
+
17
+ const props = withDefaults(defineProps<{
18
+ name : string
19
+ options: Array<Option>
20
+ modelValue?: string[]
21
+ isDisabled? : boolean
22
+ isRequired?: boolean
23
+ cssStyle?: CheckBoxStyleForEachStatus
24
+ }>(), {
25
+ modelValue: undefined,
26
+ cssStyle : undefined,
27
+ })
28
+
29
+ const checkBoxes = ref(props.options.map(option => ({
30
+ value : option.value,
31
+ label : option.label,
32
+ checked: props.modelValue?.includes(option.value) || false,
33
+ })))
34
+
35
+ const checkedValues = computed<string[]>(() => checkBoxes.value.filter(checkBox => checkBox.checked).map(checkBox => checkBox.value))
36
+ const errorMessage = ref('')
37
+ const validateInput = () => errorMessage.value = props.isRequired && checkedValues.value.length === 0 ? '必須項目です' : ''
38
+
39
+ watch(checkedValues, (newValue, oldValue) => {
40
+ const isArraysEqual = (a: string[], b: string[]): boolean => a.length === b.length && a.every((val, index) => val === b[index])
41
+ if (oldValue && isArraysEqual(newValue, oldValue)) return
42
+ validateInput()
43
+ emit('update:modelValue', newValue)
44
+ }, {
45
+ deep : true,
46
+ immediate: !!props.modelValue?.length,
47
+ })
48
+
49
+ watch(() => props.modelValue, newValue => {
50
+ if (!newValue) return
51
+ checkBoxes.value.forEach(checkBox => checkBox.checked = newValue.includes(checkBox.value))
52
+ }, {
53
+ deep : true,
54
+ immediate: true,
55
+ })
56
+
57
+ const errorColor = {
58
+ ...(props.cssStyle?.error ?? {}),
59
+ textColor : COLOR.white,
60
+ backgroundColor: COLOR.red,
61
+ border : {
62
+ color : COLOR.red,
63
+ size : '1px',
64
+ radius: '.25rem',
65
+ },
66
+ }
67
+ </script>
68
+
69
+ <template>
70
+ <div :class="$style.check_boxes">
71
+ <template
72
+ v-for="(checkBox, index) in checkBoxes"
73
+ :key="checkBox.label"
74
+ >
75
+ <LabeledCheckbox
76
+ v-model="checkBoxes[index].checked"
77
+ :name="checkBox.label"
78
+ :label="checkBox.label"
79
+ :isDisabled="props.isDisabled"
80
+ :cssStyle="props.cssStyle"
81
+ />
82
+ </template>
83
+ <ErrorMessage
84
+ :errorMessages="errorMessage ? [errorMessage] : []"
85
+ :cssStyle="{
86
+ textColor: errorColor.textColor,
87
+ backgroundColor: errorColor.backgroundColor,
88
+ }"
89
+ />
90
+ </div>
91
+ </template>
92
+
93
+ <style lang="scss" module>
94
+ .check_boxes {
95
+ display : flex;
96
+ flex-wrap: wrap;
97
+ gap : 1rem 1.5rem;
98
+ position : relative;
99
+ }
100
+ </style>
@@ -0,0 +1,127 @@
1
+ <script setup lang="ts">
2
+ import type { CheckBoxStyleForEachStatus } from '../types'
3
+ import {
4
+ ref,
5
+ computed,
6
+ watch,
7
+ } from 'vue'
8
+ import CheckIcon from '../components/Icon/CheckIcon.vue'
9
+
10
+ const emit = defineEmits<{ (e: 'update:modelValue', newValue: boolean): void }>()
11
+
12
+ const props = withDefaults(defineProps<{
13
+ name: string
14
+ modelValue?: boolean
15
+ isDisabled?: boolean
16
+ cssStyle?: CheckBoxStyleForEachStatus
17
+ isDisableAnimation?: boolean
18
+ }>(), {
19
+ cssStyle: () => ({ default: {} }),
20
+ })
21
+
22
+ const isChecked = ref(props.modelValue ?? false)
23
+
24
+ const currentCssStyle = computed(() => {
25
+ if (props.isDisabled) return props.cssStyle.disabled
26
+ else return props.cssStyle.default
27
+ })
28
+
29
+ watch(() => isChecked.value, newValue => emit('update:modelValue', newValue))
30
+ watch(() => props.modelValue, newValue => isChecked.value = newValue, { immediate: true })
31
+ </script>
32
+
33
+ <template>
34
+ <span
35
+ :class="$style.check_box"
36
+ :style="{
37
+ '--border-color': currentCssStyle?.border?.color || 'blue',
38
+ '--border-size' : currentCssStyle?.border?.size || '1px',
39
+ '--radius-size': currentCssStyle?.border?.radius || '0.25rem',
40
+ '--background-color': currentCssStyle?.backgroundColor || '#fff',
41
+ '--duration': isDisableAnimation ? '0s' : '.3s',
42
+ }"
43
+ @click="isChecked = !isChecked"
44
+ >
45
+ <input
46
+ v-model="isChecked"
47
+ type="checkbox"
48
+ :name="name"
49
+ :disabled="isDisabled"
50
+ >
51
+ <div :class="$style.check_container">
52
+ <slot name="check" />
53
+ <CheckIcon v-if="!$slots.check" />
54
+ </div>
55
+ </span>
56
+ </template>
57
+
58
+ <style lang="scss" module>
59
+ @keyframes pop {
60
+ 0% {
61
+ scale : 1;
62
+ }
63
+
64
+ 10% {
65
+ scale : .8;
66
+ }
67
+
68
+ 50% {
69
+ scale : 1.1;
70
+ }
71
+
72
+ 100% {
73
+ scale: 1;
74
+ }
75
+ }
76
+
77
+ :is(.check_box) {
78
+ display : flex;
79
+ align-items : center;
80
+ justify-content : center;
81
+ width : 1.5rem;
82
+ height : 1.5rem;
83
+ box-shadow : 0 0 0 var(--border-size) var(--border-color) inset;
84
+ border-radius : var(--radius-size);
85
+ background-color: var(--background-color);
86
+ cursor : pointer;
87
+
88
+ > input {
89
+ display : none;
90
+ }
91
+
92
+ &:has(input:disabled) {
93
+ pointer-events: none !important;
94
+
95
+ &::before {
96
+ content : '';
97
+ position : absolute;
98
+ inset : 0;
99
+ pointer-events: all;
100
+ cursor : not-allowed !important;
101
+ }
102
+ }
103
+
104
+ &:has(input:checked) {
105
+ background-color: var(--border-color);
106
+ animation: pop var(--duration) ease-out;
107
+
108
+ :is(.check_container) {
109
+ color: var(--background-color);
110
+ }
111
+ }
112
+ }
113
+
114
+ :is(.check_container) {
115
+ display : flex;
116
+ align-items : center;
117
+ justify-content: center;
118
+ width : 1rem;
119
+ height : 1rem;
120
+ color : var(--border-color);
121
+
122
+ > * {
123
+ color: currentColor;
124
+ fill : currentColor;
125
+ }
126
+ }
127
+ </style>
@@ -0,0 +1,234 @@
1
+ <script setup lang="ts">
2
+ import type { Ref } from 'vue'
3
+ import { onBeforeUnmount, ref, reactive, watch } from 'vue'
4
+ import {
5
+ MESSAGES,
6
+ composeDateValue as composeDate,
7
+ formatDateValue,
8
+ splitDate,
9
+ validateDateObject,
10
+ } from '@geckou/ui-core'
11
+ import { FormValidationManager } from '../scripts/form-validation-manager'
12
+ import InputBox from '../components/InputBox.vue'
13
+ import CalendarIcon from '../components/Icon/CalendarIcon.vue'
14
+ import ErrorMessage from '../components/ErrorMessage.vue'
15
+
16
+ const emit = defineEmits<{ (e: 'update:modelValue', newValue: string | null): void }>()
17
+ const datePicker = ref<HTMLInputElement | null>(null)
18
+ const props = withDefaults(defineProps<{
19
+ name: string
20
+ modelValue: string
21
+ isDisabled? : boolean
22
+ isRequired?: boolean
23
+ formValidationManager?: FormValidationManager | null
24
+ minDate?: string
25
+ maxDate?: string
26
+ size?: 'small' | 'medium'
27
+ type?: 'date' | 'month'
28
+ }>(), {
29
+ isDisabled : false,
30
+ formValidationManager: null,
31
+ minDate : '',
32
+ maxDate : '',
33
+ size : 'medium',
34
+ type : 'date',
35
+ })
36
+
37
+ const dateObject = reactive({
38
+ year : '',
39
+ month: '',
40
+ day : '',
41
+ })
42
+
43
+ const dateValue: Ref<string> = ref('')
44
+ const errorMessage = ref('')
45
+
46
+ const setValid = (isValid: boolean): void => {
47
+ if (props.formValidationManager) props.formValidationManager.setValid(props.name, isValid)
48
+ }
49
+
50
+ const validateInput = (value: string) => {
51
+ if (!value && props.isRequired) return { isValid: false, message: MESSAGES.required }
52
+ return { isValid: true, message: '' }
53
+ }
54
+
55
+ const validateObject = (object: {
56
+ year: string
57
+ month: string
58
+ day?: string
59
+ }) => validateDateObject(
60
+ { year: object.year, month: object.month, day: object.day ?? '' },
61
+ { type: props.type, isRequired: props.isRequired },
62
+ )
63
+
64
+ const setDateObject = (value: string): void => {
65
+ const { year, month, day } = splitDate(value)
66
+ dateObject.year = year
67
+ dateObject.month = month
68
+ // 値に日が含まれない場合は前の入力を残さない
69
+ dateObject.day = props.type === 'month' ? '' : day
70
+ }
71
+
72
+ /** 年月日から入力欄の値(YYYY-MM-DD / YYYY-MM)を組み立てる */
73
+ const composeDateValue = (): string => composeDate({ ...dateObject }, props.type)
74
+
75
+ watch(() => dateValue.value, newValue => {
76
+ setDateObject(newValue)
77
+ const { isValid, message } = validateInput(newValue)
78
+ errorMessage.value = message
79
+ emit('update:modelValue', newValue)
80
+ setValid(isValid)
81
+ })
82
+
83
+ watch(() => dateObject, newValue => {
84
+ const { isValid, message } = validateObject(newValue)
85
+ errorMessage.value = message
86
+ setValid(isValid)
87
+ if (isValid) dateValue.value = composeDateValue()
88
+ }, { deep: true })
89
+
90
+ const applyModelValue = (value: string): void => {
91
+ if (!value) {
92
+ dateValue.value = ''
93
+ setDateObject('')
94
+ errorMessage.value = ''
95
+ setValid(validateInput('').isValid)
96
+ return
97
+ }
98
+
99
+ const formatted = formatDateValue(value, props.type)
100
+ if (!formatted || formatted === dateValue.value) return
101
+
102
+ dateValue.value = formatted
103
+ setDateObject(formatted)
104
+ }
105
+
106
+ watch(() => props.modelValue, newValue => applyModelValue(newValue))
107
+
108
+ applyModelValue(props.modelValue)
109
+ setValid(validateInput(dateValue.value).isValid)
110
+
111
+ // アンマウント後も無効判定が残らないように登録を解除する
112
+ onBeforeUnmount(() => props.formValidationManager?.remove(props.name))
113
+ </script>
114
+
115
+ <template>
116
+ <InputBox
117
+ :isDisabled="isDisabled"
118
+ :class="[
119
+ $style.date_picker,
120
+ $style[size],
121
+ { [$style.is_disabled]: isDisabled },
122
+ ]"
123
+ :isErrored="!!errorMessage"
124
+ >
125
+ <div :class="$style.date_input">
126
+ <CalendarIcon :class="$style.icon" />
127
+ <input
128
+ ref="datePicker"
129
+ v-model="dateValue"
130
+ :type="type"
131
+ :name="name"
132
+ :max="maxDate"
133
+ :min="minDate"
134
+ :required="isRequired"
135
+ :disabled="isDisabled"
136
+ >
137
+ </div>
138
+ <input
139
+ v-model="dateObject.year"
140
+ placeholder="年"
141
+ maxlength="4"
142
+ type="text"
143
+ :disabled="isDisabled"
144
+ :class="$style.year"
145
+ >/
146
+ <input
147
+ v-model="dateObject.month"
148
+ placeholder="月"
149
+ maxlength="2"
150
+ type="text"
151
+ :disabled="isDisabled"
152
+ >
153
+ <span v-if="type === 'date'">/</span>
154
+ <input
155
+ v-if="type === 'date'"
156
+ v-model="dateObject.day"
157
+ placeholder="日"
158
+ maxlength="2"
159
+ type="text"
160
+ :disabled="isDisabled"
161
+ >
162
+ <ErrorMessage :errorMessages="errorMessage ? [errorMessage] : []" />
163
+ </InputBox>
164
+ </template>
165
+
166
+ <style lang="scss" module>
167
+ @use '../assets/scss/mixin' as *;
168
+
169
+ .icon {
170
+ @include icon($color: var(--link-color));
171
+ margin : auto;
172
+ position: absolute;
173
+ top : 0;
174
+ left : var(--sp-small);
175
+ bottom : 0;
176
+ }
177
+
178
+ .date_input {
179
+ flex : 0 0 auto;
180
+ position: relative;
181
+
182
+ > input {
183
+ width : calc(var(--icon-medium) + var(--sp-small) * 2);
184
+ padding: var(--sp-medium) var(--sp-small);
185
+
186
+ &[type="date"],
187
+ &[type="month"] {
188
+ opacity: 0;
189
+
190
+ &::-webkit-calendar-picker-indicator {
191
+ position: absolute;
192
+ left : 0;
193
+ width : 100%;
194
+ height : 100%;
195
+ opacity: 0;
196
+ cursor : pointer;
197
+ }
198
+ }
199
+ }
200
+ }
201
+
202
+ .date_picker {
203
+ display : flex;
204
+ align-items: center;
205
+ width : 100%;
206
+ position : relative;
207
+ line-height: 1;
208
+
209
+ > input {
210
+ padding: var(--sp-medium);
211
+ width : calc( var(--sp-medium) * 2 + 3ch);
212
+
213
+ &.year {
214
+ width: calc( var(--sp-medium) * 2 + 5ch);
215
+ }
216
+
217
+ &[type="text"] {
218
+ flex: 0 0 auto;
219
+ }
220
+ }
221
+
222
+ &.small {
223
+ .icon {
224
+ @include icon($color: var(--link-color), $size: var(--icon-small));
225
+ left: var(--sp-small);
226
+ }
227
+
228
+ input {
229
+ padding : var(--sp-min) var(--sp-small);
230
+ font-size: var(--fs-small);
231
+ }
232
+ }
233
+ }
234
+ </style>
@@ -0,0 +1,85 @@
1
+ <script setup lang="ts">
2
+ import { computed } from 'vue'
3
+ import { FormValidationManager } from '../scripts/form-validation-manager'
4
+ import DatePicker from '../components/DatePicker.vue'
5
+
6
+ type DateRange = {
7
+ start: string
8
+ end: string
9
+ }
10
+
11
+ const emit = defineEmits<{ (e: 'update:modelValue', newValue: DateRange): void }>()
12
+
13
+ const props = withDefaults(defineProps<{
14
+ name: string
15
+ modelValue?: DateRange
16
+ isDisabled?: boolean
17
+ isRequired?: boolean
18
+ formValidationManager?: FormValidationManager | null
19
+ minDate?: string
20
+ maxDate?: string
21
+ size?: 'small' | 'medium'
22
+ type?: 'date' | 'month'
23
+ }>(), {
24
+ modelValue : () => ({ start: '', end: '' }),
25
+ isDisabled : false,
26
+ isRequired : false,
27
+ formValidationManager: null,
28
+ minDate : '',
29
+ maxDate : '',
30
+ size : 'medium',
31
+ type : 'date',
32
+ })
33
+
34
+ const startDate = computed(() => props.modelValue.start)
35
+ const endDate = computed(() => props.modelValue.end)
36
+
37
+ const updateStart = (newValue: string | null) => emit('update:modelValue', { start: newValue ?? '', end: endDate.value })
38
+ const updateEnd = (newValue: string | null) => emit('update:modelValue', { start: startDate.value, end: newValue ?? '' })
39
+ </script>
40
+
41
+ <template>
42
+ <div :class="$style.date_range_picker">
43
+ <DatePicker
44
+ :name="`${name}Start`"
45
+ :modelValue="startDate"
46
+ :isDisabled="isDisabled"
47
+ :isRequired="isRequired"
48
+ :formValidationManager="formValidationManager"
49
+ :minDate="minDate"
50
+ :maxDate="endDate || maxDate"
51
+ :size="size"
52
+ :type="type"
53
+ @update:modelValue="updateStart"
54
+ />
55
+ <div :class="$style.range">
56
+
57
+ </div>
58
+ <DatePicker
59
+ :name="`${name}End`"
60
+ :modelValue="endDate"
61
+ :isDisabled="isDisabled"
62
+ :isRequired="isRequired"
63
+ :formValidationManager="formValidationManager"
64
+ :minDate="startDate || minDate"
65
+ :maxDate="maxDate"
66
+ :size="size"
67
+ :type="type"
68
+ @update:modelValue="updateEnd"
69
+ />
70
+ </div>
71
+ </template>
72
+
73
+ <style lang="scss" module>
74
+ .date_range_picker {
75
+ display : flex;
76
+ align-items: center;
77
+ gap : var(--sp-small);
78
+ flex-wrap : wrap;
79
+ }
80
+
81
+ .range {
82
+ flex : 0 0 auto;
83
+ color: var(--gray);
84
+ }
85
+ </style>