@geckou/ui-vue 0.3.1 → 0.4.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 (145) hide show
  1. package/README.md +8 -1
  2. package/dist/components/ArticleList/Card/Artistic.vue.d.ts +10 -0
  3. package/dist/components/ArticleList/Card/Entertainment.vue.d.ts +10 -0
  4. package/dist/components/ArticleList/Card/Gallery.vue.d.ts +10 -0
  5. package/dist/components/ArticleList/Card/Grid.vue.d.ts +11 -0
  6. package/dist/components/ArticleList/Card/News.vue.d.ts +9 -0
  7. package/dist/components/ArticleList/Card/Rounded.vue.d.ts +10 -0
  8. package/dist/components/ArticleList/Card/Row.vue.d.ts +9 -0
  9. package/dist/components/ArticleList/Card/Simple.vue.d.ts +10 -0
  10. package/dist/components/ArticleList/Card/Standard.vue.d.ts +10 -0
  11. package/dist/components/ArticleList/Card/Tile.vue.d.ts +10 -0
  12. package/dist/components/ArticleList/GenericArticleList.vue.d.ts +11 -0
  13. package/dist/components/ArticleList/List/Artistic.vue.d.ts +8 -0
  14. package/dist/components/ArticleList/List/Entertainment.vue.d.ts +8 -0
  15. package/dist/components/ArticleList/List/Gallery.vue.d.ts +8 -0
  16. package/dist/components/ArticleList/List/Grid.vue.d.ts +11 -0
  17. package/dist/components/ArticleList/List/News.vue.d.ts +8 -0
  18. package/dist/components/ArticleList/List/Rounded.vue.d.ts +8 -0
  19. package/dist/components/ArticleList/List/Row.vue.d.ts +8 -0
  20. package/dist/components/ArticleList/List/Simple.vue.d.ts +8 -0
  21. package/dist/components/ArticleList/List/Standard.vue.d.ts +8 -0
  22. package/dist/components/ArticleList/List/Tile.vue.d.ts +8 -0
  23. package/dist/components/ArticleList/Parts/AuthorInfo.vue.d.ts +31 -0
  24. package/dist/components/ArticleList/Parts/CardContainer.vue.d.ts +20 -0
  25. package/dist/components/ArticleList/Parts/CardHeading.vue.d.ts +12 -0
  26. package/dist/components/ArticleList/Parts/CategoryList.vue.d.ts +19 -0
  27. package/dist/components/ArticleList/Parts/ExcerptText.vue.d.ts +12 -0
  28. package/dist/components/ArticleList/Parts/MetadataList.vue.d.ts +32 -0
  29. package/dist/components/ArticleList/Parts/NoImage.vue.d.ts +2 -0
  30. package/dist/components/ArticleList/Parts/PostedDate.vue.d.ts +14 -0
  31. package/dist/components/ArticleList/Parts/TagList.vue.d.ts +18 -0
  32. package/dist/components/ArticleList/Parts/ThumbnailImage.vue.d.ts +17 -0
  33. package/dist/components/BasicButton.vue.d.ts +26 -0
  34. package/dist/components/CheckBox.vue.d.ts +34 -0
  35. package/dist/components/CheckBoxes.vue.d.ts +18 -0
  36. package/dist/components/CheckButton.vue.d.ts +31 -0
  37. package/dist/components/DatePicker.vue.d.ts +27 -0
  38. package/dist/components/DateRangePicker.vue.d.ts +31 -0
  39. package/dist/components/DateSelector.vue.d.ts +17 -0
  40. package/dist/components/DropdownUi.vue.d.ts +37 -0
  41. package/dist/components/ErrorMessage.vue.d.ts +9 -0
  42. package/dist/components/Icon/CalendarIcon.vue.d.ts +2 -0
  43. package/dist/components/Icon/CheckIcon.vue.d.ts +2 -0
  44. package/dist/components/Icon/CloseIcon.vue.d.ts +2 -0
  45. package/dist/components/Icon/Folder.vue.d.ts +2 -0
  46. package/dist/components/Icon/Image.vue.d.ts +2 -0
  47. package/dist/components/Icon/KeyboardArrowDownIcon.vue.d.ts +2 -0
  48. package/dist/components/Icon/Tag.vue.d.ts +2 -0
  49. package/dist/components/InputBox.vue.d.ts +27 -0
  50. package/dist/components/InputGroup.vue.d.ts +17 -0
  51. package/dist/components/LabeledCheckbox.vue.d.ts +20 -0
  52. package/dist/components/LabeledFieldset.vue.d.ts +21 -0
  53. package/dist/components/LoadingSpinner.vue.d.ts +2 -0
  54. package/dist/components/ModalBox.vue.d.ts +27 -0
  55. package/dist/components/PopupBox.vue.d.ts +30 -0
  56. package/dist/components/RadioButtons.vue.d.ts +20 -0
  57. package/dist/components/SelectBox.vue.d.ts +37 -0
  58. package/dist/components/SlideDownUi.vue.d.ts +37 -0
  59. package/dist/components/TabUI.vue.d.ts +54 -0
  60. package/dist/components/TextArea.vue.d.ts +33 -0
  61. package/dist/components/TextBox.vue.d.ts +44 -0
  62. package/dist/components/TextButton.vue.d.ts +10 -0
  63. package/dist/components/ToggleButton.vue.d.ts +23 -0
  64. package/dist/const/index.d.ts +1 -0
  65. package/dist/const/list-theme.d.ts +62 -0
  66. package/dist/index.d.ts +43 -0
  67. package/dist/index.js +3519 -0
  68. package/dist/scripts/form-validation-manager.d.ts +30 -0
  69. package/dist/scripts/unique-id.d.ts +1 -0
  70. package/dist/scripts/use-click-outside.d.ts +13 -0
  71. package/dist/scripts/utils.d.ts +15 -0
  72. package/dist/style.css +1 -0
  73. package/dist/types/index.d.ts +48 -77
  74. package/package.json +16 -8
  75. package/dist/assets/scss/functions.scss +0 -6
  76. package/dist/assets/scss/mixin.scss +0 -33
  77. package/dist/components/ArticleList/Card/Artistic.vue +0 -311
  78. package/dist/components/ArticleList/Card/Entertainment.vue +0 -245
  79. package/dist/components/ArticleList/Card/Gallery.vue +0 -255
  80. package/dist/components/ArticleList/Card/Grid.vue +0 -248
  81. package/dist/components/ArticleList/Card/News.vue +0 -151
  82. package/dist/components/ArticleList/Card/Rounded.vue +0 -155
  83. package/dist/components/ArticleList/Card/Row.vue +0 -168
  84. package/dist/components/ArticleList/Card/Simple.vue +0 -233
  85. package/dist/components/ArticleList/Card/Standard.vue +0 -188
  86. package/dist/components/ArticleList/Card/Tile.vue +0 -172
  87. package/dist/components/ArticleList/GenericArticleList.vue +0 -143
  88. package/dist/components/ArticleList/List/Artistic.vue +0 -24
  89. package/dist/components/ArticleList/List/Entertainment.vue +0 -24
  90. package/dist/components/ArticleList/List/Gallery.vue +0 -24
  91. package/dist/components/ArticleList/List/Grid.vue +0 -216
  92. package/dist/components/ArticleList/List/News.vue +0 -24
  93. package/dist/components/ArticleList/List/Rounded.vue +0 -24
  94. package/dist/components/ArticleList/List/Row.vue +0 -24
  95. package/dist/components/ArticleList/List/Simple.vue +0 -24
  96. package/dist/components/ArticleList/List/Standard.vue +0 -24
  97. package/dist/components/ArticleList/List/Tile.vue +0 -24
  98. package/dist/components/ArticleList/Parts/AuthorInfo.vue +0 -100
  99. package/dist/components/ArticleList/Parts/CardContainer.vue +0 -23
  100. package/dist/components/ArticleList/Parts/CardHeading.vue +0 -43
  101. package/dist/components/ArticleList/Parts/CategoryList.vue +0 -41
  102. package/dist/components/ArticleList/Parts/ExcerptText.vue +0 -43
  103. package/dist/components/ArticleList/Parts/MetadataList.vue +0 -120
  104. package/dist/components/ArticleList/Parts/NoImage.vue +0 -36
  105. package/dist/components/ArticleList/Parts/PostedDate.vue +0 -41
  106. package/dist/components/ArticleList/Parts/TagList.vue +0 -41
  107. package/dist/components/ArticleList/Parts/ThumbnailImage.vue +0 -85
  108. package/dist/components/BasicButton.vue +0 -143
  109. package/dist/components/CheckBox.vue +0 -140
  110. package/dist/components/CheckBoxes.vue +0 -101
  111. package/dist/components/CheckButton.vue +0 -127
  112. package/dist/components/DatePicker.vue +0 -234
  113. package/dist/components/DateRangePicker.vue +0 -85
  114. package/dist/components/DateSelector.vue +0 -273
  115. package/dist/components/DropdownUi.vue +0 -118
  116. package/dist/components/ErrorMessage.vue +0 -61
  117. package/dist/components/Icon/CalendarIcon.vue +0 -8
  118. package/dist/components/Icon/CheckIcon.vue +0 -8
  119. package/dist/components/Icon/CloseIcon.vue +0 -8
  120. package/dist/components/Icon/Folder.vue +0 -8
  121. package/dist/components/Icon/Image.vue +0 -14
  122. package/dist/components/Icon/KeyboardArrowDownIcon.vue +0 -8
  123. package/dist/components/Icon/Tag.vue +0 -8
  124. package/dist/components/InputBox.vue +0 -134
  125. package/dist/components/InputGroup.vue +0 -41
  126. package/dist/components/LabeledCheckbox.vue +0 -82
  127. package/dist/components/LabeledFieldset.vue +0 -41
  128. package/dist/components/LoadingSpinner.vue +0 -56
  129. package/dist/components/ModalBox.vue +0 -192
  130. package/dist/components/PopupBox.vue +0 -93
  131. package/dist/components/RadioButtons.vue +0 -163
  132. package/dist/components/SelectBox.vue +0 -166
  133. package/dist/components/SlideDownUi.vue +0 -123
  134. package/dist/components/TabUI.vue +0 -123
  135. package/dist/components/TextArea.vue +0 -115
  136. package/dist/components/TextBox.vue +0 -111
  137. package/dist/components/TextButton.vue +0 -47
  138. package/dist/components/ToggleButton.vue +0 -179
  139. package/dist/const/index.ts +0 -2
  140. package/dist/const/list-theme.ts +0 -62
  141. package/dist/index.ts +0 -126
  142. package/dist/scripts/form-validation-manager.ts +0 -63
  143. package/dist/scripts/utils.ts +0 -44
  144. package/dist/types/custom.d.ts +0 -1
  145. package/dist/types/vue-shim.d.ts +0 -5
@@ -1,127 +0,0 @@
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>
@@ -1,234 +0,0 @@
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>
@@ -1,85 +0,0 @@
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>
@@ -1,273 +0,0 @@
1
- <script setup lang="ts">
2
- import type { Ref } from 'vue'
3
- import { ref, computed, onBeforeUnmount, watch, watchEffect } from 'vue'
4
- import { daysInMonth, splitDate } from '@geckou/ui-core'
5
- import { FormValidationManager } from '../scripts/form-validation-manager'
6
- import InputBox from '../components/InputBox.vue'
7
- import KeyboardArrowDownIcon from '../components/Icon/KeyboardArrowDownIcon.vue'
8
- import TextButton from '../components/TextButton.vue'
9
-
10
- const emit = defineEmits<{ (e: 'update:modelValue', newValue: string): void }>()
11
-
12
- type Date = {
13
- year: string
14
- month: string
15
- day?: string
16
- }
17
- const props = withDefaults(defineProps<{
18
- name: string
19
- modelValue: string
20
- isRequired?: boolean
21
- formValidationManager?: FormValidationManager | null
22
- type?: 'date' | 'month'
23
- }>(), {
24
- formValidationManager: null,
25
- type : 'date',
26
- })
27
-
28
- const birthday: Ref<Date> = ref({
29
- year : '',
30
- month: '',
31
- day : '',
32
- })
33
-
34
- const yearsOptions = computed(() => {
35
- const today = new Date()
36
- const maxYear = today.getFullYear() - 100
37
- const currentYear = today.getFullYear() - 14
38
- const years = Array.from({ length: currentYear - maxYear + 1 }, (_, i) => (maxYear + i).toString())
39
- return years.map(year => ({ label: year, value: year }))
40
- })
41
-
42
- const monthOptions = computed(() => {
43
- return Array.from({ length: 12 }, (_, i) => {
44
- const month = (i + 1).toString()
45
- return {
46
- label: month,
47
- value: month.padStart(2, '0'),
48
- }
49
- })
50
- })
51
-
52
- const daysInSelectedMonth = computed(() => {
53
- const month = Number(birthday.value?.month)
54
- if (!month) return 31
55
-
56
- // 年が未選択のときは閏年を含む最大日数になるよう 2000 年を使う
57
- const year = Number(birthday.value?.year) || 2000
58
- return daysInMonth(year, month)
59
- })
60
-
61
- const dayOptions = computed(() => {
62
- const days = []
63
- const daysInMouth = daysInSelectedMonth.value
64
-
65
- for (let day = 1; day <= daysInMouth; day++) {
66
- const dayString = day.toString()
67
- days.push(dayString)
68
- }
69
-
70
- return days.map(day => ({
71
- label: day,
72
- value: String(day).padStart(2, '0'),
73
- }))
74
- })
75
-
76
- const openDropdown = (e: MouseEvent) => {
77
- const target = e.currentTarget as HTMLElement
78
- const select = target.firstElementChild as HTMLSelectElement
79
- if (select) {
80
- select.click()
81
- }
82
- }
83
-
84
- const selectItem = (event: Event, key: 'year' | 'day' | 'month') => {
85
- const target = event.target as HTMLSelectElement
86
- const { value } = target
87
- birthday.value = { ...birthday.value, [key]: value }
88
- }
89
-
90
- // 月や年を変えて日が存在しなくなった場合は、その月の末日に丸める
91
- watch(daysInSelectedMonth, days => {
92
- const day = Number(birthday.value.day)
93
- if (day && day > days) birthday.value = { ...birthday.value, day: String(days).padStart(2, '0') }
94
- })
95
-
96
- const EMPTY_BIRTHDAY = { year: '', month: '', day: '' }
97
-
98
- // value が空に戻されたときもリセットする(以前は前の選択が残っていた)
99
- watchEffect(() => {
100
- birthday.value = props.modelValue
101
- ? splitDate(props.modelValue)
102
- : { ...EMPTY_BIRTHDAY }
103
- })
104
-
105
- const setValid = (isValid: boolean): void => {
106
- if (props.formValidationManager) props.formValidationManager.setValid(props.name, isValid)
107
- }
108
-
109
- setValid(!props.isRequired)
110
-
111
- watch(() => birthday.value, newValue => {
112
- const isFilled = Boolean(newValue.year && newValue.month && (props.type === 'month' || newValue.day))
113
- const isEmpty = !newValue.year && !newValue.month && !newValue.day
114
-
115
- setValid(props.isRequired ? isFilled : isFilled || isEmpty)
116
-
117
- if (isEmpty) emit('update:modelValue', '')
118
- else if (!isFilled) return
119
- else {
120
- const parts = props.type === 'month'
121
- ? [newValue.year, newValue.month]
122
- : [newValue.year, newValue.month, newValue.day]
123
- emit('update:modelValue', parts.filter(Boolean).join('-'))
124
- }
125
- }, { deep: true })
126
-
127
- // アンマウント後も無効判定が残らないように登録を解除する
128
- onBeforeUnmount(() => props.formValidationManager?.remove(props.name))
129
- </script>
130
-
131
- <template>
132
- <InputBox :class="$style.date_selector">
133
- <div
134
- :class="[$style.selector_wrapper]"
135
- @click="openDropdown($event)"
136
- >
137
- <select
138
- :value="birthday.year"
139
- :class="$style.year"
140
- @change="selectItem($event, 'year')"
141
- >
142
- <option
143
- disabled
144
- selected
145
- value=""
146
- >
147
-
148
- </option>
149
- <option
150
- v-for="year in yearsOptions"
151
- :key="year.value"
152
- :value="year.value"
153
- >
154
- {{ year.label }}
155
- </option>
156
- </select>
157
- <KeyboardArrowDownIcon />
158
- </div>
159
- <div
160
- :class="[$style.selector_wrapper]"
161
- @click="openDropdown($event)"
162
- >
163
- <select
164
- :value="birthday.month"
165
- @change="selectItem($event, 'month')"
166
- >
167
- <option
168
- disabled
169
- selected
170
- value=""
171
- >
172
-
173
- </option>
174
- <option
175
- v-for="month in monthOptions"
176
- :key="month.value"
177
- :value="month.value"
178
- >
179
- {{ month.label }}
180
- </option>
181
- </select>
182
- <KeyboardArrowDownIcon />
183
- </div>
184
- <div
185
- v-if="type === 'date'"
186
- :class="[$style.selector_wrapper]"
187
- @click="openDropdown($event)"
188
- >
189
- <select
190
- :value="birthday.day"
191
- @change="selectItem($event, 'day')"
192
- >
193
- <option
194
- disabled
195
- selected
196
- value=""
197
- >
198
-
199
- </option>
200
- <option
201
- v-for="day in dayOptions"
202
- :key="day.value"
203
- :value="day.value"
204
- >
205
- {{ day.label }}
206
- </option>
207
- </select>
208
- <KeyboardArrowDownIcon />
209
- </div>
210
- <TextButton
211
- text="削除"
212
- variant="caution"
213
- :class="$style.delete_button"
214
- @click="birthday = { year: '', month: '', day: '' }"
215
- />
216
- </InputBox>
217
- </template>
218
-
219
- <style lang="scss" module>
220
- @use '../assets/scss/mixin' as *;
221
-
222
- .date_selector {
223
- width : max-content;
224
- display : flex;
225
- align-items: center;
226
- position : relative;
227
-
228
- > * {
229
- &:not(:last-of-type) {
230
- &:after {
231
- content: '/';
232
- }
233
- }
234
- }
235
- }
236
-
237
- .selector_wrapper {
238
- position: relative;
239
- width : max-content;
240
-
241
- > svg {
242
- @include icon($color: var(--link-color));
243
- position : absolute;
244
- margin : auto;
245
- top : 0;
246
- right : var(--sp-small);
247
- bottom : 0;
248
- pointer-events: none;
249
- }
250
-
251
- &:has(select:focus) {
252
- svg {
253
- rotate: 180deg;
254
- }
255
- }
256
-
257
- > select {
258
- width : calc(3ch + (var(--sp-medium) * 2) + var(--icon-medium));
259
- padding : var(--sp-medium);
260
- padding-inline-end: calc((var(--sp-small) * 2) + var(--icon-medium));
261
- cursor : pointer;
262
-
263
- &.year {
264
- width: calc(5ch + (var(--sp-medium) * 2) + var(--icon-medium));
265
- }
266
- }
267
- }
268
-
269
- .delete_button {
270
- margin-inline: var(--sp-medium);
271
- font-size : var(--fs-small);
272
- }
273
- </style>