@14ch/svelte-ui 0.0.13 → 0.0.14
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.
- package/dist/assets/styles/variables.scss +2 -2
- package/dist/components/Button.svelte +31 -5
- package/dist/components/Button.svelte.d.ts +5 -3
- package/dist/components/Checkbox.svelte +2 -3
- package/dist/components/Checkbox.svelte.d.ts +1 -1
- package/dist/components/CheckboxGroup.svelte +6 -3
- package/dist/components/CheckboxGroup.svelte.d.ts +1 -1
- package/dist/components/ColorPicker.svelte +4 -5
- package/dist/components/ColorPicker.svelte.d.ts +3 -3
- package/dist/components/Combobox.svelte +26 -7
- package/dist/components/Combobox.svelte.d.ts +1 -1
- package/dist/components/ConfirmDialog.svelte +3 -7
- package/dist/components/ConfirmDialog.svelte.d.ts +1 -1
- package/dist/components/Datepicker.svelte +88 -24
- package/dist/components/Datepicker.svelte.d.ts +4 -3
- package/dist/components/DatepickerCalendar.svelte +1 -1
- package/dist/components/DatepickerCalendar.svelte.d.ts +1 -1
- package/dist/components/Drawer.svelte +1 -1
- package/dist/components/Fab.svelte +28 -11
- package/dist/components/Fab.svelte.d.ts +8 -4
- package/dist/components/FileUploader.svelte +1 -1
- package/dist/components/FileUploader.svelte.d.ts +1 -1
- package/dist/components/IconButton.svelte +4 -3
- package/dist/components/IconButton.svelte.d.ts +4 -3
- package/dist/components/ImageUploader.svelte +1 -1
- package/dist/components/ImageUploader.svelte.d.ts +1 -1
- package/dist/components/Input.svelte +5 -3
- package/dist/components/Input.svelte.d.ts +4 -3
- package/dist/components/Popup.svelte +69 -82
- package/dist/components/Popup.svelte.d.ts +3 -3
- package/dist/components/PopupMenu.svelte +40 -56
- package/dist/components/PopupMenu.svelte.d.ts +3 -3
- package/dist/components/PopupMenuButton.svelte +10 -23
- package/dist/components/PopupMenuButton.svelte.d.ts +5 -4
- package/dist/components/Radio.svelte +1 -1
- package/dist/components/Radio.svelte.d.ts +1 -1
- package/dist/components/RadioGroup.svelte +1 -1
- package/dist/components/RadioGroup.svelte.d.ts +1 -1
- package/dist/components/SegmentedControl.svelte +1 -2
- package/dist/components/SegmentedControl.svelte.d.ts +1 -1
- package/dist/components/Select.svelte +1 -1
- package/dist/components/Select.svelte.d.ts +1 -1
- package/dist/components/Slider.svelte +2 -3
- package/dist/components/Slider.svelte.d.ts +1 -1
- package/dist/components/Snackbar.svelte +3 -2
- package/dist/components/Snackbar.svelte.d.ts +3 -2
- package/dist/components/SnackbarItem.svelte +4 -3
- package/dist/components/SnackbarItem.svelte.d.ts +4 -3
- package/dist/components/Switch.svelte +2 -4
- package/dist/components/Switch.svelte.d.ts +1 -1
- package/dist/components/TabItem.svelte +2 -2
- package/dist/components/Textarea.svelte +14 -12
- package/dist/components/Textarea.svelte.d.ts +4 -3
- package/dist/components/skeleton/SkeletonAvatar.svelte +22 -32
- package/dist/components/skeleton/SkeletonAvatar.svelte.d.ts +6 -2
- package/dist/components/skeleton/SkeletonButton.svelte +18 -16
- package/dist/components/skeleton/SkeletonButton.svelte.d.ts +5 -2
- package/dist/components/skeleton/SkeletonHeading.svelte +15 -18
- package/dist/components/skeleton/SkeletonHeading.svelte.d.ts +3 -2
- package/dist/components/skeleton/SkeletonMedia.svelte +29 -41
- package/dist/components/skeleton/SkeletonMedia.svelte.d.ts +8 -2
- package/dist/components/skeleton/SkeletonText.svelte +12 -14
- package/dist/components/skeleton/SkeletonText.svelte.d.ts +4 -2
- package/dist/i18n/index.d.ts +143 -5
- package/dist/i18n/index.js +20 -32
- package/dist/i18n/locales/de.d.ts +35 -0
- package/dist/i18n/locales/de.js +35 -0
- package/dist/i18n/locales/es.d.ts +35 -0
- package/dist/i18n/locales/es.js +35 -0
- package/dist/i18n/locales/fr.d.ts +35 -0
- package/dist/i18n/locales/fr.js +35 -0
- package/dist/i18n/locales/zh-cn.d.ts +35 -0
- package/dist/i18n/locales/zh-cn.js +35 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +1 -0
- package/dist/types/menuItem.d.ts +1 -1
- package/dist/types/propOptions.d.ts +54 -0
- package/dist/types/propOptions.js +5 -0
- package/dist/utils/popupManager.d.ts +26 -0
- package/dist/utils/popupManager.js +34 -0
- package/package.json +1 -1
- /package/dist/types/{eventHandlers.d.ts → callbackHandlers.d.ts} +0 -0
- /package/dist/types/{eventHandlers.js → callbackHandlers.js} +0 -0
|
@@ -610,8 +610,8 @@
|
|
|
610
610
|
/* Drawer */
|
|
611
611
|
--svelte-ui-drawer-gap: 16px;
|
|
612
612
|
--svelte-ui-drawer-gap-sm: 8px;
|
|
613
|
-
--svelte-ui-drawer-padding: 16px 24px;
|
|
614
|
-
--svelte-ui-drawer-body-padding:
|
|
613
|
+
--svelte-ui-drawer-header-padding: 16px 24px;
|
|
614
|
+
--svelte-ui-drawer-body-padding: 0;
|
|
615
615
|
--svelte-ui-drawer-header-height: 56px;
|
|
616
616
|
--svelte-ui-drawer-title-font-size: 1.4rem;
|
|
617
617
|
--svelte-ui-drawer-description-font-size: 0.875rem;
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
MouseHandler,
|
|
11
11
|
TouchHandler,
|
|
12
12
|
PointerHandler
|
|
13
|
-
} from '../types/
|
|
13
|
+
} from '../types/callbackHandlers';
|
|
14
|
+
import type { ButtonVariant, ButtonSize } from '../types/propOptions';
|
|
14
15
|
import Icon from './Icon.svelte';
|
|
15
16
|
import LoadingSpinner from './LoadingSpinner.svelte';
|
|
16
17
|
import { getStyleFromNumber } from '../utils/style';
|
|
@@ -29,10 +30,11 @@
|
|
|
29
30
|
|
|
30
31
|
// スタイル/レイアウト
|
|
31
32
|
customStyle?: HTMLButtonAttributes['style'];
|
|
32
|
-
variant?:
|
|
33
|
-
size?:
|
|
33
|
+
variant?: ButtonVariant;
|
|
34
|
+
size?: ButtonSize;
|
|
34
35
|
color?: string;
|
|
35
36
|
fullWidth?: boolean;
|
|
37
|
+
align?: 'left' | 'center' | 'right';
|
|
36
38
|
minWidth?: string | number;
|
|
37
39
|
rounded?: boolean;
|
|
38
40
|
popup?: boolean;
|
|
@@ -107,6 +109,7 @@
|
|
|
107
109
|
size = 'medium',
|
|
108
110
|
color,
|
|
109
111
|
fullWidth = false,
|
|
112
|
+
align = 'center',
|
|
110
113
|
minWidth = 0,
|
|
111
114
|
rounded = false,
|
|
112
115
|
popup = false,
|
|
@@ -310,6 +313,7 @@
|
|
|
310
313
|
popup && 'button--popup',
|
|
311
314
|
rounded && 'button--rounded',
|
|
312
315
|
fullWidth && 'button--full-width',
|
|
316
|
+
`button--align-${align}`,
|
|
313
317
|
loading && 'button--loading',
|
|
314
318
|
reducedMotion && 'button--no-motion'
|
|
315
319
|
]
|
|
@@ -453,6 +457,14 @@
|
|
|
453
457
|
width: 100%;
|
|
454
458
|
}
|
|
455
459
|
|
|
460
|
+
.button--align-left {
|
|
461
|
+
justify-content: flex-start;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.button--align-right {
|
|
465
|
+
justify-content: flex-end;
|
|
466
|
+
}
|
|
467
|
+
|
|
456
468
|
.button--rounded {
|
|
457
469
|
border-radius: var(--svelte-ui-button-border-radius-rounded);
|
|
458
470
|
}
|
|
@@ -510,9 +522,8 @@
|
|
|
510
522
|
}
|
|
511
523
|
|
|
512
524
|
.button__label {
|
|
513
|
-
width: 100%;
|
|
514
525
|
display: block;
|
|
515
|
-
|
|
526
|
+
width: auto;
|
|
516
527
|
line-height: var(--svelte-ui-button-line-height);
|
|
517
528
|
text-box-trim: trim-both;
|
|
518
529
|
text-box-edge: cap alphabetic;
|
|
@@ -531,6 +542,11 @@
|
|
|
531
542
|
justify-content: center;
|
|
532
543
|
}
|
|
533
544
|
|
|
545
|
+
.button--align-left .button__popup-icon {
|
|
546
|
+
margin-left: auto;
|
|
547
|
+
margin-right: -4px;
|
|
548
|
+
}
|
|
549
|
+
|
|
534
550
|
.button__loading {
|
|
535
551
|
position: absolute;
|
|
536
552
|
top: 50%;
|
|
@@ -550,6 +566,11 @@
|
|
|
550
566
|
margin-right: -2px;
|
|
551
567
|
}
|
|
552
568
|
|
|
569
|
+
.button--align-left.button--small .button__popup-icon {
|
|
570
|
+
margin-left: auto;
|
|
571
|
+
margin-right: -2px;
|
|
572
|
+
}
|
|
573
|
+
|
|
553
574
|
.button--large .button__icon {
|
|
554
575
|
margin-left: -6px;
|
|
555
576
|
}
|
|
@@ -558,6 +579,11 @@
|
|
|
558
579
|
margin-right: -6px;
|
|
559
580
|
}
|
|
560
581
|
|
|
582
|
+
.button--align-left.button--large .button__popup-icon {
|
|
583
|
+
margin-left: auto;
|
|
584
|
+
margin-right: -6px;
|
|
585
|
+
}
|
|
586
|
+
|
|
561
587
|
/* Reduced motion */
|
|
562
588
|
.button--no-motion,
|
|
563
589
|
.button--no-motion:before,
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
3
3
|
import type { IconVariant, IconWeight, IconGrade, IconOpticalSize } from '../types/icon';
|
|
4
|
-
import type { FocusHandler, KeyboardHandler, MouseHandler, TouchHandler, PointerHandler } from '../types/
|
|
4
|
+
import type { FocusHandler, KeyboardHandler, MouseHandler, TouchHandler, PointerHandler } from '../types/callbackHandlers';
|
|
5
|
+
import type { ButtonVariant, ButtonSize } from '../types/propOptions';
|
|
5
6
|
export type ButtonProps = {
|
|
6
7
|
children: Snippet;
|
|
7
8
|
buttonAttributes?: HTMLButtonAttributes | undefined;
|
|
8
9
|
type?: HTMLButtonAttributes['type'];
|
|
9
10
|
tabindex?: number | null;
|
|
10
11
|
customStyle?: HTMLButtonAttributes['style'];
|
|
11
|
-
variant?:
|
|
12
|
-
size?:
|
|
12
|
+
variant?: ButtonVariant;
|
|
13
|
+
size?: ButtonSize;
|
|
13
14
|
color?: string;
|
|
14
15
|
fullWidth?: boolean;
|
|
16
|
+
align?: 'left' | 'center' | 'right';
|
|
15
17
|
minWidth?: string | number;
|
|
16
18
|
rounded?: boolean;
|
|
17
19
|
popup?: boolean;
|
|
@@ -8,9 +8,8 @@
|
|
|
8
8
|
KeyboardHandler,
|
|
9
9
|
MouseHandler,
|
|
10
10
|
TouchHandler,
|
|
11
|
-
PointerHandler
|
|
12
|
-
|
|
13
|
-
} from '../types/eventHandlers';
|
|
11
|
+
PointerHandler
|
|
12
|
+
} from '../types/callbackHandlers';
|
|
14
13
|
|
|
15
14
|
// =========================================================================
|
|
16
15
|
// Props, States & Constants
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type Snippet } from 'svelte';
|
|
2
2
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
3
|
-
import type { FocusHandler, KeyboardHandler, MouseHandler, TouchHandler, PointerHandler } from '../types/
|
|
3
|
+
import type { FocusHandler, KeyboardHandler, MouseHandler, TouchHandler, PointerHandler } from '../types/callbackHandlers';
|
|
4
4
|
export type CheckboxProps = {
|
|
5
5
|
children?: Snippet;
|
|
6
6
|
name?: string;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import Checkbox from './Checkbox.svelte';
|
|
6
6
|
import { onMount } from 'svelte';
|
|
7
7
|
import { getStyleFromNumber } from '../utils/style';
|
|
8
|
-
import type { BivariantValueHandler } from '../types/
|
|
8
|
+
import type { BivariantValueHandler } from '../types/callbackHandlers';
|
|
9
9
|
|
|
10
10
|
// =========================================================================
|
|
11
11
|
// Props, States & Constants
|
|
@@ -78,8 +78,11 @@
|
|
|
78
78
|
onchange(value);
|
|
79
79
|
};
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
// =========================================================================
|
|
82
|
+
// $defived
|
|
83
|
+
// =========================================================================
|
|
84
|
+
const gapStyle = $derived(gap !== undefined ? getStyleFromNumber(gap) : undefined);
|
|
85
|
+
const minOptionWidthStyle = $derived(getStyleFromNumber(minOptionWidth));
|
|
83
86
|
</script>
|
|
84
87
|
|
|
85
88
|
<ul
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Option, OptionValue } from '../types/options';
|
|
2
|
-
import type { BivariantValueHandler } from '../types/
|
|
2
|
+
import type { BivariantValueHandler } from '../types/callbackHandlers';
|
|
3
3
|
export type CheckboxGroupProps = {
|
|
4
4
|
options: Option[];
|
|
5
5
|
value: OptionValue[];
|
|
@@ -11,9 +11,8 @@
|
|
|
11
11
|
KeyboardHandler,
|
|
12
12
|
MouseHandler,
|
|
13
13
|
TouchHandler,
|
|
14
|
-
PointerHandler
|
|
15
|
-
|
|
16
|
-
} from '../types/eventHandlers';
|
|
14
|
+
PointerHandler
|
|
15
|
+
} from '../types/callbackHandlers';
|
|
17
16
|
|
|
18
17
|
// =========================================================================
|
|
19
18
|
// Props, States & Constants
|
|
@@ -40,8 +39,8 @@
|
|
|
40
39
|
iconVariant?: IconVariant;
|
|
41
40
|
|
|
42
41
|
// 入力イベント
|
|
43
|
-
onchange?:
|
|
44
|
-
oninput?:
|
|
42
|
+
onchange?: (value: string) => void;
|
|
43
|
+
oninput?: (value: string) => void;
|
|
45
44
|
|
|
46
45
|
// フォーカスイベント
|
|
47
46
|
onfocus?: FocusHandler;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
2
2
|
import type { IconVariant } from '../types/icon';
|
|
3
|
-
import type { FocusHandler, KeyboardHandler, MouseHandler, TouchHandler, PointerHandler
|
|
3
|
+
import type { FocusHandler, KeyboardHandler, MouseHandler, TouchHandler, PointerHandler } from '../types/callbackHandlers';
|
|
4
4
|
export type ColorPickerProps = {
|
|
5
5
|
value: string;
|
|
6
6
|
id?: string;
|
|
@@ -14,8 +14,8 @@ export type ColorPickerProps = {
|
|
|
14
14
|
clearable?: boolean;
|
|
15
15
|
clearButtonAriaLabel?: string;
|
|
16
16
|
iconVariant?: IconVariant;
|
|
17
|
-
onchange?:
|
|
18
|
-
oninput?:
|
|
17
|
+
onchange?: (value: string) => void;
|
|
18
|
+
oninput?: (value: string) => void;
|
|
19
19
|
onfocus?: FocusHandler;
|
|
20
20
|
onblur?: FocusHandler;
|
|
21
21
|
onkeydown?: KeyboardHandler;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
TouchHandler,
|
|
14
14
|
PointerHandler,
|
|
15
15
|
BivariantValueHandler
|
|
16
|
-
} from '../types/
|
|
16
|
+
} from '../types/callbackHandlers';
|
|
17
17
|
|
|
18
18
|
// =========================================================================
|
|
19
19
|
// Props, States & Constants
|
|
@@ -165,10 +165,7 @@
|
|
|
165
165
|
let popupRef = $state<any>();
|
|
166
166
|
let highlightedIndex = $state(-1);
|
|
167
167
|
let isFocused = $state(false);
|
|
168
|
-
|
|
169
|
-
// 各要素のIDを生成
|
|
170
|
-
const inputId = `${id}-input`;
|
|
171
|
-
const listboxId = `${id}-listbox`;
|
|
168
|
+
let isKeyboardNavigation = $state(false);
|
|
172
169
|
|
|
173
170
|
// =========================================================================
|
|
174
171
|
// Methods
|
|
@@ -180,6 +177,7 @@
|
|
|
180
177
|
popupRef?.close();
|
|
181
178
|
highlightedIndex = -1;
|
|
182
179
|
isFocused = false;
|
|
180
|
+
isKeyboardNavigation = false;
|
|
183
181
|
|
|
184
182
|
// スクリーンリーダーアナウンス
|
|
185
183
|
if (option !== null && option !== undefined) {
|
|
@@ -239,6 +237,7 @@
|
|
|
239
237
|
switch (event.key) {
|
|
240
238
|
case 'ArrowDown':
|
|
241
239
|
event?.preventDefault?.();
|
|
240
|
+
isKeyboardNavigation = true;
|
|
242
241
|
if (!isFocused) {
|
|
243
242
|
isFocused = true;
|
|
244
243
|
popupRef?.open();
|
|
@@ -247,6 +246,7 @@
|
|
|
247
246
|
break;
|
|
248
247
|
case 'ArrowUp':
|
|
249
248
|
event?.preventDefault?.();
|
|
249
|
+
isKeyboardNavigation = true;
|
|
250
250
|
if (!isFocused) {
|
|
251
251
|
isFocused = true;
|
|
252
252
|
popupRef?.open();
|
|
@@ -263,6 +263,7 @@
|
|
|
263
263
|
event?.preventDefault?.();
|
|
264
264
|
inputValue = '';
|
|
265
265
|
highlightedIndex = -1;
|
|
266
|
+
isKeyboardNavigation = false;
|
|
266
267
|
popupRef?.close();
|
|
267
268
|
isFocused = false;
|
|
268
269
|
break;
|
|
@@ -387,6 +388,9 @@
|
|
|
387
388
|
// =========================================================================
|
|
388
389
|
// $derived
|
|
389
390
|
// =========================================================================
|
|
391
|
+
// 各要素のIDを生成
|
|
392
|
+
const inputId = $derived(`${id}-input`);
|
|
393
|
+
const listboxId = $derived(`${id}-listbox`);
|
|
390
394
|
|
|
391
395
|
// フィルタリングされたオプション
|
|
392
396
|
const filteredOptions = $derived.by(() => {
|
|
@@ -463,7 +467,7 @@
|
|
|
463
467
|
bind:this={popupRef}
|
|
464
468
|
anchorElement={comboboxElement}
|
|
465
469
|
position="bottom-left"
|
|
466
|
-
|
|
470
|
+
mobileFullscreen={false}
|
|
467
471
|
onClose={handlePopupClose}
|
|
468
472
|
margin={4}
|
|
469
473
|
>
|
|
@@ -481,6 +485,7 @@
|
|
|
481
485
|
type="button"
|
|
482
486
|
class="combobox__option"
|
|
483
487
|
class:combobox__option--highlighted={index === highlightedIndex}
|
|
488
|
+
class:combobox__option--keyboard={isKeyboardNavigation && index === highlightedIndex}
|
|
484
489
|
class:combobox__option--selected={option === inputValue}
|
|
485
490
|
role="option"
|
|
486
491
|
aria-selected={option === inputValue}
|
|
@@ -489,7 +494,10 @@
|
|
|
489
494
|
event?.stopPropagation?.();
|
|
490
495
|
selectOption(option);
|
|
491
496
|
}}
|
|
492
|
-
onmouseenter={() =>
|
|
497
|
+
onmouseenter={() => {
|
|
498
|
+
highlightedIndex = index;
|
|
499
|
+
isKeyboardNavigation = false; // マウス操作時はキーボード操作フラグをリセット
|
|
500
|
+
}}
|
|
493
501
|
>
|
|
494
502
|
{option}
|
|
495
503
|
</button>
|
|
@@ -553,6 +561,12 @@
|
|
|
553
561
|
background-color: var(--svelte-ui-combobox-option-hover-bg);
|
|
554
562
|
}
|
|
555
563
|
|
|
564
|
+
/* キーボード操作時のみ枠線を表示 */
|
|
565
|
+
&.combobox__option--keyboard {
|
|
566
|
+
outline: var(--svelte-ui-focus-outline-inner);
|
|
567
|
+
outline-offset: var(--svelte-ui-focus-outline-offset-inner);
|
|
568
|
+
}
|
|
569
|
+
|
|
556
570
|
&.combobox__option--selected {
|
|
557
571
|
background-color: var(--svelte-ui-combobox-option-selected-bg);
|
|
558
572
|
}
|
|
@@ -561,6 +575,11 @@
|
|
|
561
575
|
background-color: var(--svelte-ui-combobox-option-selected-bg);
|
|
562
576
|
}
|
|
563
577
|
|
|
578
|
+
&.combobox__option--selected.combobox__option--keyboard {
|
|
579
|
+
outline: var(--svelte-ui-focus-outline-inner);
|
|
580
|
+
outline-offset: var(--svelte-ui-focus-outline-offset-inner);
|
|
581
|
+
}
|
|
582
|
+
|
|
564
583
|
&:focus,
|
|
565
584
|
&:focus-visible {
|
|
566
585
|
outline: var(--svelte-ui-focus-outline-inner);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
2
|
-
import type { FocusHandler, KeyboardHandler, MouseHandler, TouchHandler, PointerHandler, BivariantValueHandler } from '../types/
|
|
2
|
+
import type { FocusHandler, KeyboardHandler, MouseHandler, TouchHandler, PointerHandler, BivariantValueHandler } from '../types/callbackHandlers';
|
|
3
3
|
export type ComboboxProps = {
|
|
4
4
|
name?: string;
|
|
5
5
|
value: string | number | null | undefined;
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
id?: string;
|
|
21
21
|
|
|
22
22
|
// スタイル/レイアウト
|
|
23
|
-
|
|
23
|
+
danger?: boolean;
|
|
24
24
|
width?: string | number;
|
|
25
25
|
|
|
26
26
|
// 状態/動作
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
id,
|
|
44
44
|
|
|
45
45
|
// スタイル/レイアウト
|
|
46
|
-
|
|
46
|
+
danger = false,
|
|
47
47
|
width = 400,
|
|
48
48
|
|
|
49
49
|
// 状態/動作
|
|
@@ -101,11 +101,7 @@
|
|
|
101
101
|
</Button>
|
|
102
102
|
<Button
|
|
103
103
|
variant="filled"
|
|
104
|
-
color={
|
|
105
|
-
? 'var(--svelte-ui-danger-color)'
|
|
106
|
-
: variant === 'warning'
|
|
107
|
-
? 'var(--svelte-ui-warning-color)'
|
|
108
|
-
: undefined}
|
|
104
|
+
color={danger ? 'var(--svelte-ui-danger-color)' : undefined}
|
|
109
105
|
onclick={handleSubmit}
|
|
110
106
|
>
|
|
111
107
|
{submitLabel}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
import Popup from './Popup.svelte';
|
|
15
15
|
import DatepickerCalendar from './DatepickerCalendar.svelte';
|
|
16
16
|
import { announceToScreenReader } from '../utils/accessibility';
|
|
17
|
+
import { getLocale } from '../config';
|
|
17
18
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
18
19
|
import type { IconVariant, IconWeight, IconGrade, IconOpticalSize } from '../types/icon';
|
|
19
20
|
import type {
|
|
@@ -23,7 +24,8 @@
|
|
|
23
24
|
MouseHandler,
|
|
24
25
|
TouchHandler,
|
|
25
26
|
PointerHandler
|
|
26
|
-
} from '../types/
|
|
27
|
+
} from '../types/callbackHandlers';
|
|
28
|
+
import type { DatepickerMode, FocusStyle } from '../types/propOptions';
|
|
27
29
|
|
|
28
30
|
dayjs.extend(localeData);
|
|
29
31
|
|
|
@@ -44,7 +46,7 @@
|
|
|
44
46
|
|
|
45
47
|
// スタイル/レイアウト
|
|
46
48
|
inline?: boolean;
|
|
47
|
-
focusStyle?:
|
|
49
|
+
focusStyle?: FocusStyle;
|
|
48
50
|
fullWidth?: boolean;
|
|
49
51
|
rounded?: boolean;
|
|
50
52
|
|
|
@@ -58,7 +60,7 @@
|
|
|
58
60
|
|
|
59
61
|
// 状態/動作
|
|
60
62
|
disabled?: boolean;
|
|
61
|
-
mode?:
|
|
63
|
+
mode?: DatepickerMode;
|
|
62
64
|
/** テキスト入力を有効にするか */
|
|
63
65
|
enableTextInput?: boolean;
|
|
64
66
|
/** クリック時にポップアップを開くかどうかを有効にするか */
|
|
@@ -104,7 +106,7 @@
|
|
|
104
106
|
value = $bindable(),
|
|
105
107
|
format,
|
|
106
108
|
nullString = '',
|
|
107
|
-
locale
|
|
109
|
+
locale,
|
|
108
110
|
rangeSeparator = ' - ',
|
|
109
111
|
|
|
110
112
|
// HTML属性系
|
|
@@ -184,8 +186,6 @@
|
|
|
184
186
|
let openedViaKeyboard: boolean = $state(false);
|
|
185
187
|
let displayValue = $state('');
|
|
186
188
|
|
|
187
|
-
const calendarId = `${id}-calendar`;
|
|
188
|
-
|
|
189
189
|
const localeConfig = {
|
|
190
190
|
en: {
|
|
191
191
|
defaultFormat: 'MM/DD/YYYY (ddd)',
|
|
@@ -234,12 +234,22 @@
|
|
|
234
234
|
// =========================================================================
|
|
235
235
|
// Effects
|
|
236
236
|
// =========================================================================
|
|
237
|
+
const effectiveLocale = $derived(locale ?? getLocale());
|
|
238
|
+
|
|
237
239
|
$effect(() => {
|
|
238
|
-
|
|
240
|
+
// range モードのときは、value を「start <= end」の順序に正規化
|
|
241
|
+
if (mode === 'range' && value && 'start' in value && 'end' in value) {
|
|
242
|
+
const startDay = dayjs(value.start).locale(effectiveLocale);
|
|
243
|
+
const endDay = dayjs(value.end).locale(effectiveLocale);
|
|
244
|
+
if (startDay.isAfter(endDay)) {
|
|
245
|
+
value = { start: value.end, end: value.start };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
239
248
|
});
|
|
240
249
|
|
|
241
250
|
$effect(() => {
|
|
242
|
-
const formatWithLocale = (date: Date) =>
|
|
251
|
+
const formatWithLocale = (date: Date) =>
|
|
252
|
+
dayjs(date).locale(effectiveLocale).format(finalFormat);
|
|
243
253
|
|
|
244
254
|
if (mode === 'range' && value && 'start' in value && 'end' in value) {
|
|
245
255
|
displayValue = `${formatWithLocale(value.start)}${rangeSeparator}${formatWithLocale(value.end)}`;
|
|
@@ -257,11 +267,11 @@
|
|
|
257
267
|
// スクリーンリーダーアナウンス
|
|
258
268
|
if (value) {
|
|
259
269
|
if (mode === 'range' && typeof value === 'object' && 'start' in value && 'end' in value) {
|
|
260
|
-
const startDate = dayjs(value.start).format(finalFormat);
|
|
261
|
-
const endDate = dayjs(value.end).format(finalFormat);
|
|
270
|
+
const startDate = dayjs(value.start).locale(effectiveLocale).format(finalFormat);
|
|
271
|
+
const endDate = dayjs(value.end).locale(effectiveLocale).format(finalFormat);
|
|
262
272
|
announceToScreenReader(`Date range selected: ${startDate} to ${endDate}`);
|
|
263
273
|
} else if (value instanceof Date) {
|
|
264
|
-
const formattedDate = dayjs(value).format(finalFormat);
|
|
274
|
+
const formattedDate = dayjs(value).locale(effectiveLocale).format(finalFormat);
|
|
265
275
|
announceToScreenReader(`Date selected: ${formattedDate}`);
|
|
266
276
|
}
|
|
267
277
|
}
|
|
@@ -285,8 +295,13 @@
|
|
|
285
295
|
const handleClick = (event: MouseEvent) => {
|
|
286
296
|
if (disabled) return;
|
|
287
297
|
if (enableClickToOpen) {
|
|
288
|
-
|
|
289
|
-
|
|
298
|
+
const isOpen = popupRef?.getIsOpen && popupRef.getIsOpen();
|
|
299
|
+
if (isOpen) {
|
|
300
|
+
close();
|
|
301
|
+
} else {
|
|
302
|
+
openedViaKeyboard = false;
|
|
303
|
+
open();
|
|
304
|
+
}
|
|
290
305
|
}
|
|
291
306
|
onclick?.(event);
|
|
292
307
|
};
|
|
@@ -487,24 +502,73 @@
|
|
|
487
502
|
if (!enableTextInput) return;
|
|
488
503
|
|
|
489
504
|
const inputStr = String(inputValue ?? '').trim();
|
|
505
|
+
|
|
506
|
+
// 空文字列の場合は値をクリア
|
|
490
507
|
if (!inputStr) {
|
|
491
508
|
value = undefined;
|
|
492
509
|
onchange(value);
|
|
493
510
|
return;
|
|
494
511
|
}
|
|
495
512
|
|
|
496
|
-
//
|
|
497
|
-
|
|
498
|
-
|
|
513
|
+
// 日付パース処理
|
|
514
|
+
if (mode === 'range') {
|
|
515
|
+
const parsedRange = parseRangeInput(inputStr, effectiveLocale);
|
|
516
|
+
if (!parsedRange) return;
|
|
499
517
|
|
|
500
|
-
|
|
501
|
-
|
|
518
|
+
value = parsedRange;
|
|
519
|
+
onchange(value);
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// single モードでは先頭の「日付本体」のみを解釈する
|
|
524
|
+
const parsedSingle = parseSingleInput(inputStr, effectiveLocale);
|
|
525
|
+
if (!parsedSingle) return;
|
|
502
526
|
|
|
527
|
+
value = parsedSingle;
|
|
528
|
+
onchange(value);
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
const parseSingleInput = (inputStr: string, locale: string): Date | null => {
|
|
532
|
+
const parseFormat = currentLocaleConfig.defaultFormat;
|
|
533
|
+
const datePart = extractDatePart(inputStr);
|
|
503
534
|
const parsedDate = dayjs(datePart, parseFormat, locale, true);
|
|
504
|
-
if (parsedDate.isValid())
|
|
505
|
-
|
|
506
|
-
|
|
535
|
+
if (!parsedDate.isValid()) return null;
|
|
536
|
+
return parsedDate.toDate();
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
const parseRangeInput = (inputStr: string, locale: string): { start: Date; end: Date } | null => {
|
|
540
|
+
const parseFormat = currentLocaleConfig.rangeFormat;
|
|
541
|
+
const dateLikeParts = inputStr.match(/[0-90-9./-]+/g) ?? [];
|
|
542
|
+
|
|
543
|
+
if (dateLikeParts.length < 2) {
|
|
544
|
+
return null;
|
|
507
545
|
}
|
|
546
|
+
|
|
547
|
+
const startRaw = extractDatePart(dateLikeParts[0]);
|
|
548
|
+
const endRaw = extractDatePart(dateLikeParts[1]);
|
|
549
|
+
|
|
550
|
+
const startParsed = dayjs(startRaw, parseFormat, locale, true);
|
|
551
|
+
const endParsed = dayjs(endRaw, parseFormat, locale, true);
|
|
552
|
+
|
|
553
|
+
if (!startParsed.isValid() || !endParsed.isValid()) {
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
const startDate = startParsed.toDate();
|
|
558
|
+
const endDate = endParsed.toDate();
|
|
559
|
+
|
|
560
|
+
// start > end の場合はここで入れ替えて正規化して返す
|
|
561
|
+
return startDate <= endDate
|
|
562
|
+
? { start: startDate, end: endDate }
|
|
563
|
+
: { start: endDate, end: startDate };
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
const extractDatePart = (raw: string): string => {
|
|
567
|
+
// 入力値のうち「日付として有効な部分」(数字と区切り記号)だけを抽出してパースする。
|
|
568
|
+
// 曜日やカッコなどの装飾はここで無視される。
|
|
569
|
+
const trimmed = raw.trim();
|
|
570
|
+
const match = trimmed.match(/^[0-90-9./-]+/);
|
|
571
|
+
return match ? match[0] : trimmed;
|
|
508
572
|
};
|
|
509
573
|
|
|
510
574
|
export const open = () => {
|
|
@@ -524,8 +588,8 @@
|
|
|
524
588
|
// =========================================================================
|
|
525
589
|
// $derived
|
|
526
590
|
// =========================================================================
|
|
527
|
-
|
|
528
|
-
const currentLocaleConfig = $derived(localeConfig[
|
|
591
|
+
const calendarId = $derived(`${id}-calendar`);
|
|
592
|
+
const currentLocaleConfig = $derived(localeConfig[effectiveLocale]);
|
|
529
593
|
const finalFormat = $derived(
|
|
530
594
|
format ||
|
|
531
595
|
(mode === 'range' ? currentLocaleConfig.rangeFormat : currentLocaleConfig.defaultFormat)
|
|
@@ -606,7 +670,7 @@
|
|
|
606
670
|
onchange={handleChange}
|
|
607
671
|
{minDate}
|
|
608
672
|
{maxDate}
|
|
609
|
-
{
|
|
673
|
+
locale={effectiveLocale}
|
|
610
674
|
id={calendarId}
|
|
611
675
|
/>
|
|
612
676
|
</Popup>
|
|
@@ -6,7 +6,8 @@ import 'dayjs/locale/es';
|
|
|
6
6
|
import 'dayjs/locale/zh-cn';
|
|
7
7
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
8
8
|
import type { IconVariant, IconWeight, IconGrade, IconOpticalSize } from '../types/icon';
|
|
9
|
-
import type { BivariantValueHandler, FocusHandler, KeyboardHandler, MouseHandler, TouchHandler, PointerHandler } from '../types/
|
|
9
|
+
import type { BivariantValueHandler, FocusHandler, KeyboardHandler, MouseHandler, TouchHandler, PointerHandler } from '../types/callbackHandlers';
|
|
10
|
+
import type { DatepickerMode, FocusStyle } from '../types/propOptions';
|
|
10
11
|
export type DatepickerProps = {
|
|
11
12
|
value: Date | {
|
|
12
13
|
start: Date;
|
|
@@ -19,7 +20,7 @@ export type DatepickerProps = {
|
|
|
19
20
|
id?: string;
|
|
20
21
|
inputAttributes?: HTMLInputAttributes | undefined;
|
|
21
22
|
inline?: boolean;
|
|
22
|
-
focusStyle?:
|
|
23
|
+
focusStyle?: FocusStyle;
|
|
23
24
|
fullWidth?: boolean;
|
|
24
25
|
rounded?: boolean;
|
|
25
26
|
hasIcon?: boolean;
|
|
@@ -29,7 +30,7 @@ export type DatepickerProps = {
|
|
|
29
30
|
iconOpticalSize?: IconOpticalSize;
|
|
30
31
|
iconVariant?: IconVariant;
|
|
31
32
|
disabled?: boolean;
|
|
32
|
-
mode?:
|
|
33
|
+
mode?: DatepickerMode;
|
|
33
34
|
/** テキスト入力を有効にするか */
|
|
34
35
|
enableTextInput?: boolean;
|
|
35
36
|
/** クリック時にポップアップを開くかどうかを有効にするか */
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
import IconButton from './IconButton.svelte';
|
|
15
15
|
import { onMount } from 'svelte';
|
|
16
16
|
import { t } from '../i18n';
|
|
17
|
-
import type { BivariantValueHandler } from '../types/
|
|
17
|
+
import type { BivariantValueHandler } from '../types/callbackHandlers';
|
|
18
18
|
|
|
19
19
|
dayjs.extend(localeData);
|
|
20
20
|
dayjs.extend(isSameOrBefore);
|
|
@@ -4,7 +4,7 @@ import 'dayjs/locale/fr';
|
|
|
4
4
|
import 'dayjs/locale/de';
|
|
5
5
|
import 'dayjs/locale/es';
|
|
6
6
|
import 'dayjs/locale/zh-cn';
|
|
7
|
-
import type { BivariantValueHandler } from '../types/
|
|
7
|
+
import type { BivariantValueHandler } from '../types/callbackHandlers';
|
|
8
8
|
export type DatepickerCalendarProps = {
|
|
9
9
|
value: Date | {
|
|
10
10
|
start: Date;
|
|
@@ -310,7 +310,7 @@
|
|
|
310
310
|
align-items: center;
|
|
311
311
|
justify-content: stretch;
|
|
312
312
|
min-height: var(--svelte-ui-drawer-header-height);
|
|
313
|
-
padding: var(--svelte-ui-drawer-padding);
|
|
313
|
+
padding: var(--svelte-ui-drawer-header-padding);
|
|
314
314
|
margin-bottom: calc(0px - var(--svelte-ui-drawer-body-padding));
|
|
315
315
|
}
|
|
316
316
|
.drawer__header .drawer__title {
|