@14ch/svelte-ui 0.0.13 → 0.0.15
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 +3 -7
- package/dist/components/Button.svelte +31 -5
- package/dist/components/Button.svelte.d.ts +5 -3
- package/dist/components/Checkbox.svelte +4 -4
- 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 +18 -10
- package/dist/components/ColorPicker.svelte.d.ts +4 -4
- package/dist/components/Combobox.svelte +28 -8
- 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 +8 -4
- package/dist/components/Drawer.svelte.d.ts +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/Icon.svelte +20 -24
- package/dist/components/Icon.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 +110 -99
- package/dist/components/Input.svelte.d.ts +5 -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 +3 -2
- 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 +4 -5
- package/dist/components/SegmentedControl.svelte.d.ts +1 -1
- package/dist/components/Select.svelte +2 -2
- 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/Tab.svelte +1 -0
- package/dist/components/TabItem.svelte +24 -3
- package/dist/components/TabItem.svelte.d.ts +1 -0
- package/dist/components/Textarea.svelte +74 -38
- 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 -6
- package/dist/i18n/index.js +18 -40
- 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/formatText.d.ts +2 -2
- package/dist/utils/formatText.js +2 -2
- 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
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
TouchHandler,
|
|
16
16
|
PointerHandler,
|
|
17
17
|
BivariantValueHandler
|
|
18
|
-
} from '../types/
|
|
18
|
+
} from '../types/callbackHandlers';
|
|
19
|
+
import type { FocusStyle } from '../types/propOptions';
|
|
19
20
|
|
|
20
21
|
// =========================================================================
|
|
21
22
|
// Props, States & Constants
|
|
@@ -23,7 +24,7 @@
|
|
|
23
24
|
export type InputProps = {
|
|
24
25
|
// 基本プロパティ
|
|
25
26
|
name?: string;
|
|
26
|
-
value: string | number;
|
|
27
|
+
value: string | number | null | undefined;
|
|
27
28
|
|
|
28
29
|
// HTML属性系
|
|
29
30
|
id?: string | null;
|
|
@@ -41,7 +42,7 @@
|
|
|
41
42
|
|
|
42
43
|
// スタイル/レイアウト
|
|
43
44
|
inline?: boolean;
|
|
44
|
-
focusStyle?:
|
|
45
|
+
focusStyle?: FocusStyle;
|
|
45
46
|
placeholder?: string;
|
|
46
47
|
fullWidth?: boolean;
|
|
47
48
|
width?: string | number | null;
|
|
@@ -49,6 +50,7 @@
|
|
|
49
50
|
maxWidth?: string | number | null;
|
|
50
51
|
rounded?: boolean;
|
|
51
52
|
customStyle?: string;
|
|
53
|
+
unit?: string;
|
|
52
54
|
|
|
53
55
|
// アイコン関連
|
|
54
56
|
rightIcon?: string;
|
|
@@ -136,12 +138,13 @@
|
|
|
136
138
|
// スタイル/レイアウト
|
|
137
139
|
inline = false,
|
|
138
140
|
focusStyle = 'outline',
|
|
139
|
-
customStyle = '',
|
|
140
141
|
fullWidth = false,
|
|
141
142
|
width = null,
|
|
142
143
|
minWidth = inline ? null : 120,
|
|
143
144
|
maxWidth = null,
|
|
144
145
|
rounded = false,
|
|
146
|
+
customStyle = '',
|
|
147
|
+
unit = '',
|
|
145
148
|
|
|
146
149
|
// アイコン関連
|
|
147
150
|
rightIcon = undefined,
|
|
@@ -380,16 +383,24 @@
|
|
|
380
383
|
// =========================================================================
|
|
381
384
|
// $derived
|
|
382
385
|
// =========================================================================
|
|
383
|
-
const
|
|
386
|
+
const displayValue = $derived.by(() => {
|
|
387
|
+
if (!value) {
|
|
388
|
+
if (inline && !placeholder) {
|
|
389
|
+
return ' ';
|
|
390
|
+
}
|
|
391
|
+
return '';
|
|
392
|
+
}
|
|
384
393
|
if (type === 'number' && typeof value === 'number') {
|
|
385
394
|
return value.toLocaleString();
|
|
386
395
|
}
|
|
387
|
-
return
|
|
388
|
-
};
|
|
396
|
+
return convertToHtmlWithLink(value);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
const isLinkifyActive = $derived(linkify && (type === 'text' || type === 'url'));
|
|
389
400
|
|
|
390
401
|
const linkHtmlValue = $derived.by(() => {
|
|
391
|
-
if (!
|
|
392
|
-
const result = convertToHtmlWithLink(
|
|
402
|
+
if (!isLinkifyActive) return '';
|
|
403
|
+
const result = convertToHtmlWithLink(value);
|
|
393
404
|
return typeof result === 'string' ? result : String(result ?? '');
|
|
394
405
|
});
|
|
395
406
|
|
|
@@ -403,7 +414,7 @@
|
|
|
403
414
|
input--focus-{focusStyle}
|
|
404
415
|
input--type-{type}"
|
|
405
416
|
class:input--inline={inline}
|
|
406
|
-
class:input--linkify={
|
|
417
|
+
class:input--linkify={isLinkifyActive}
|
|
407
418
|
class:input--auto-resize={inline}
|
|
408
419
|
class:input--full-width={fullWidth}
|
|
409
420
|
class:input--clearable={clearable}
|
|
@@ -417,12 +428,17 @@
|
|
|
417
428
|
data-testid="input"
|
|
418
429
|
style="width: {widthStyle}; max-width: {maxWidthStyle}; min-width: {minWidthStyle}"
|
|
419
430
|
>
|
|
420
|
-
<!--
|
|
421
|
-
{
|
|
422
|
-
<div class="input__display-text"
|
|
423
|
-
{
|
|
431
|
+
<!-- 表示用テキスト -->
|
|
432
|
+
<div class="input__display-text" data-placeholder={placeholder} style={customStyle}>
|
|
433
|
+
<div class="input__display-text-content">
|
|
434
|
+
{@html displayValue}
|
|
435
|
+
{#if type === 'number' && unit !== ''}
|
|
436
|
+
<span class="input__unit-text">
|
|
437
|
+
{unit}
|
|
438
|
+
</span>
|
|
439
|
+
{/if}
|
|
424
440
|
</div>
|
|
425
|
-
|
|
441
|
+
</div>
|
|
426
442
|
<!-- 入力用要素 -->
|
|
427
443
|
<div class="input__wrapper">
|
|
428
444
|
<input
|
|
@@ -476,9 +492,11 @@
|
|
|
476
492
|
{...restProps}
|
|
477
493
|
/>
|
|
478
494
|
</div>
|
|
479
|
-
{#if
|
|
495
|
+
{#if isLinkifyActive}
|
|
480
496
|
<div class="input__link-text" style={customStyle}>
|
|
481
|
-
|
|
497
|
+
<div class="input__link-text-content">
|
|
498
|
+
{@html linkHtmlValue}
|
|
499
|
+
</div>
|
|
482
500
|
</div>
|
|
483
501
|
{/if}
|
|
484
502
|
<!-- クリアボタン -->
|
|
@@ -486,7 +504,7 @@
|
|
|
486
504
|
<div class="input__clear-button">
|
|
487
505
|
<IconButton
|
|
488
506
|
ariaLabel={t('input.clear')}
|
|
489
|
-
color="var(--svelte-ui-
|
|
507
|
+
color="var(--svelte-ui-text-color)"
|
|
490
508
|
onclick={(event) => {
|
|
491
509
|
event.stopPropagation();
|
|
492
510
|
clear();
|
|
@@ -566,12 +584,19 @@
|
|
|
566
584
|
.input {
|
|
567
585
|
display: inline-block;
|
|
568
586
|
position: relative;
|
|
587
|
+
vertical-align: top;
|
|
569
588
|
width: auto;
|
|
589
|
+
height: var(--svelte-ui-input-height);
|
|
570
590
|
max-width: 100%;
|
|
571
591
|
height: inherit;
|
|
572
592
|
}
|
|
573
593
|
|
|
574
594
|
.input__wrapper {
|
|
595
|
+
position: absolute;
|
|
596
|
+
top: 0;
|
|
597
|
+
left: 0;
|
|
598
|
+
width: 100%;
|
|
599
|
+
height: 100%;
|
|
575
600
|
padding: inherit;
|
|
576
601
|
border: none;
|
|
577
602
|
font-size: inherit;
|
|
@@ -579,7 +604,7 @@
|
|
|
579
604
|
color: inherit;
|
|
580
605
|
line-height: inherit;
|
|
581
606
|
text-align: inherit;
|
|
582
|
-
opacity:
|
|
607
|
+
opacity: 1;
|
|
583
608
|
}
|
|
584
609
|
|
|
585
610
|
/* =============================================
|
|
@@ -608,63 +633,40 @@
|
|
|
608
633
|
}
|
|
609
634
|
}
|
|
610
635
|
|
|
611
|
-
.input__display-text
|
|
612
|
-
|
|
613
|
-
|
|
636
|
+
.input__display-text,
|
|
637
|
+
.input__link-text {
|
|
638
|
+
display: flex;
|
|
639
|
+
align-items: center;
|
|
614
640
|
width: 100%;
|
|
615
641
|
min-width: 1em;
|
|
616
642
|
padding: inherit;
|
|
617
|
-
background: inherit;
|
|
618
|
-
border: inherit;
|
|
619
643
|
font-size: inherit;
|
|
620
644
|
font-weight: inherit;
|
|
621
645
|
color: inherit;
|
|
622
646
|
line-height: inherit;
|
|
623
647
|
text-align: inherit;
|
|
624
648
|
white-space: nowrap;
|
|
625
|
-
vertical-align: top;
|
|
626
649
|
overflow: hidden;
|
|
627
650
|
text-overflow: ellipsis;
|
|
628
651
|
opacity: 1;
|
|
629
652
|
transition: none;
|
|
630
|
-
cursor: text !important;
|
|
631
|
-
|
|
632
|
-
&::before {
|
|
633
|
-
content: '';
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
&:empty::before {
|
|
637
|
-
content: attr(data-placeholder);
|
|
638
|
-
}
|
|
639
653
|
}
|
|
640
654
|
|
|
641
|
-
/* リンク表示用オーバーレイ */
|
|
642
655
|
.input__link-text {
|
|
643
656
|
position: absolute;
|
|
644
657
|
top: 0;
|
|
645
658
|
left: 0;
|
|
646
|
-
width: 100%;
|
|
647
659
|
height: 100%;
|
|
648
|
-
display: flex;
|
|
649
|
-
align-items: center;
|
|
650
|
-
padding: inherit;
|
|
651
|
-
background: transparent;
|
|
652
|
-
border-radius: inherit;
|
|
653
|
-
font-size: inherit;
|
|
654
|
-
font-weight: inherit;
|
|
655
|
-
color: inherit;
|
|
656
|
-
line-height: inherit;
|
|
657
|
-
text-align: inherit;
|
|
658
|
-
white-space: nowrap;
|
|
659
|
-
overflow: hidden;
|
|
660
|
-
text-overflow: ellipsis;
|
|
661
660
|
pointer-events: none;
|
|
662
661
|
z-index: 1;
|
|
663
662
|
}
|
|
664
663
|
|
|
665
664
|
.input__link-text :global(a) {
|
|
666
665
|
pointer-events: auto;
|
|
667
|
-
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
.input__unit-text {
|
|
669
|
+
font-size: var(--svelte-ui-input-unit-font-size);
|
|
668
670
|
}
|
|
669
671
|
|
|
670
672
|
.input__clear-button {
|
|
@@ -739,26 +741,45 @@
|
|
|
739
741
|
}
|
|
740
742
|
|
|
741
743
|
/* =============================================
|
|
742
|
-
*
|
|
744
|
+
* タイプ別スタイル
|
|
743
745
|
* ============================================= */
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
746
|
+
/* type-number */
|
|
747
|
+
.input--type-number {
|
|
748
|
+
.input__display-text,
|
|
749
|
+
.input__link-text {
|
|
750
|
+
justify-content: flex-end;
|
|
748
751
|
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/* type-password */
|
|
755
|
+
.input--type-password {
|
|
756
|
+
&:not(.input--linkify) input {
|
|
757
|
+
color: inherit;
|
|
758
|
+
caret-color: inherit;
|
|
759
|
+
text-shadow: inherit;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
.input__display-text {
|
|
763
|
+
opacity: 0;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
749
766
|
|
|
767
|
+
/* =============================================
|
|
768
|
+
* デザインバリアント:default
|
|
769
|
+
* ============================================= */
|
|
770
|
+
.input:not(.input--inline) {
|
|
750
771
|
input {
|
|
751
772
|
min-height: var(--svelte-ui-input-height);
|
|
752
773
|
background-color: var(--svelte-ui-input-bg);
|
|
753
774
|
box-shadow: 0 0 0 var(--svelte-ui-border-width) inset var(--svelte-ui-input-border-color);
|
|
754
775
|
border: none;
|
|
755
776
|
border-radius: var(--svelte-ui-input-border-radius);
|
|
756
|
-
color: var(--svelte-ui-input-text-color);
|
|
757
777
|
}
|
|
758
778
|
|
|
759
779
|
input,
|
|
760
780
|
.input__display-text,
|
|
761
781
|
.input__link-text {
|
|
782
|
+
height: var(--svelte-ui-input-height);
|
|
762
783
|
padding: var(--svelte-ui-input-padding);
|
|
763
784
|
}
|
|
764
785
|
|
|
@@ -804,34 +825,10 @@
|
|
|
804
825
|
}
|
|
805
826
|
}
|
|
806
827
|
|
|
807
|
-
/* linkify=true かつフォーカスがないときは、input のテキストカラーだけ透明にして二重描画を防ぐ */
|
|
808
|
-
.input--linkify:not(.input--focused) input {
|
|
809
|
-
color: transparent;
|
|
810
|
-
caret-color: transparent;
|
|
811
|
-
text-shadow: none;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
/* フォーカス時はリンク用オーバーレイも非表示にして(display:none)、リンクが反応しないようにする */
|
|
815
|
-
.input--focused .input__link-text {
|
|
816
|
-
display: none;
|
|
817
|
-
}
|
|
818
|
-
|
|
819
828
|
/* =============================================
|
|
820
829
|
* デザインバリアント:inline
|
|
821
830
|
* ============================================= */
|
|
822
831
|
.input--inline {
|
|
823
|
-
&.input--type-number .input__display-text {
|
|
824
|
-
text-align: right;
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
.input__wrapper {
|
|
828
|
-
position: absolute;
|
|
829
|
-
top: 0;
|
|
830
|
-
left: 0;
|
|
831
|
-
width: 100%;
|
|
832
|
-
height: 100%;
|
|
833
|
-
}
|
|
834
|
-
|
|
835
832
|
&.input--has-left-icon {
|
|
836
833
|
input,
|
|
837
834
|
.input__display-text,
|
|
@@ -863,25 +860,6 @@
|
|
|
863
860
|
padding-right: var(--svelte-ui-input-icon-space-double-inline);
|
|
864
861
|
}
|
|
865
862
|
}
|
|
866
|
-
|
|
867
|
-
&.input--focused {
|
|
868
|
-
.input__display-text {
|
|
869
|
-
opacity: 0;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
.input__wrapper {
|
|
873
|
-
opacity: 1;
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
/* inline + linkify のときは、display-text を常に隠し、wrapper を常に表示 */
|
|
879
|
-
.input--inline.input--linkify .input__display-text {
|
|
880
|
-
opacity: 0;
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
.input--inline.input--linkify .input__wrapper {
|
|
884
|
-
opacity: 1;
|
|
885
863
|
}
|
|
886
864
|
|
|
887
865
|
/* =============================================
|
|
@@ -922,6 +900,39 @@
|
|
|
922
900
|
}
|
|
923
901
|
}
|
|
924
902
|
|
|
903
|
+
/* not-linkify + not-focused: inputを不可視化、display-textを表示 */
|
|
904
|
+
/* type=password のときは除外(常に input を表示) */
|
|
905
|
+
.input:not(.input--linkify):not(.input--focused):not(.input--type-password) input {
|
|
906
|
+
color: transparent;
|
|
907
|
+
caret-color: transparent;
|
|
908
|
+
text-shadow: none;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/* not-linkify + focused: display-textをopacity: 0 */
|
|
912
|
+
.input:not(.input--linkify).input--focused .input__display-text {
|
|
913
|
+
opacity: 0;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
/* linkify + not-focused: inputを不可視化、display-textをopacity: 0、link-textを表示 */
|
|
917
|
+
.input--linkify:not(.input--focused) input {
|
|
918
|
+
color: transparent;
|
|
919
|
+
caret-color: transparent;
|
|
920
|
+
text-shadow: none;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
.input--linkify:not(.input--focused) .input__display-text {
|
|
924
|
+
opacity: 0;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/* linkify + focused: display-textをopacity: 0、link-textをdisplay: none */
|
|
928
|
+
.input--linkify.input--focused .input__display-text {
|
|
929
|
+
opacity: 0;
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
.input--focused .input__link-text {
|
|
933
|
+
display: none;
|
|
934
|
+
}
|
|
935
|
+
|
|
925
936
|
/* =============================================
|
|
926
937
|
* プレースホルダー・テキスト表示
|
|
927
938
|
* ============================================= */
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
2
2
|
import type { IconVariant, IconWeight, IconGrade, IconOpticalSize } from '../types/icon';
|
|
3
|
-
import type { FocusHandler, KeyboardHandler, MouseHandler, TouchHandler, PointerHandler, BivariantValueHandler } from '../types/
|
|
3
|
+
import type { FocusHandler, KeyboardHandler, MouseHandler, TouchHandler, PointerHandler, BivariantValueHandler } from '../types/callbackHandlers';
|
|
4
|
+
import type { FocusStyle } from '../types/propOptions';
|
|
4
5
|
export type InputProps = {
|
|
5
6
|
name?: string;
|
|
6
|
-
value: string | number;
|
|
7
|
+
value: string | number | null | undefined;
|
|
7
8
|
id?: string | null;
|
|
8
9
|
type?: 'text' | 'password' | 'email' | 'tel' | 'url' | 'number';
|
|
9
10
|
tabindex?: number | null;
|
|
@@ -17,7 +18,7 @@ export type InputProps = {
|
|
|
17
18
|
spellcheck?: boolean | null;
|
|
18
19
|
inputAttributes?: HTMLInputAttributes | undefined;
|
|
19
20
|
inline?: boolean;
|
|
20
|
-
focusStyle?:
|
|
21
|
+
focusStyle?: FocusStyle;
|
|
21
22
|
placeholder?: string;
|
|
22
23
|
fullWidth?: boolean;
|
|
23
24
|
width?: string | number | null;
|
|
@@ -25,6 +26,7 @@ export type InputProps = {
|
|
|
25
26
|
maxWidth?: string | number | null;
|
|
26
27
|
rounded?: boolean;
|
|
27
28
|
customStyle?: string;
|
|
29
|
+
unit?: string;
|
|
28
30
|
rightIcon?: string;
|
|
29
31
|
leftIcon?: string;
|
|
30
32
|
leftIconAriaLabel?: string;
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
import { onDestroy, tick, onMount } from 'svelte';
|
|
19
19
|
import { isMobileDevice, disableBodyScroll, getViewportSize } from '../utils/mobile';
|
|
20
20
|
import { announceOpenClose } from '../utils/accessibility';
|
|
21
|
+
import { popupManager } from '../utils/popupManager';
|
|
22
|
+
import type { PopupPosition } from '../types/propOptions';
|
|
21
23
|
|
|
22
24
|
// =========================================================================
|
|
23
25
|
// Props, States & Constants
|
|
@@ -27,7 +29,7 @@
|
|
|
27
29
|
children: Snippet;
|
|
28
30
|
|
|
29
31
|
// DOM参照
|
|
30
|
-
anchorElement: HTMLElement;
|
|
32
|
+
anchorElement: HTMLElement | null | undefined;
|
|
31
33
|
|
|
32
34
|
// 基本プロパティ
|
|
33
35
|
role?: string;
|
|
@@ -36,24 +38,7 @@
|
|
|
36
38
|
id?: string;
|
|
37
39
|
|
|
38
40
|
// スタイル/レイアウト
|
|
39
|
-
position?:
|
|
40
|
-
| 'top'
|
|
41
|
-
| 'bottom'
|
|
42
|
-
| 'left'
|
|
43
|
-
| 'right'
|
|
44
|
-
| 'top-left'
|
|
45
|
-
| 'top-center'
|
|
46
|
-
| 'top-right'
|
|
47
|
-
| 'bottom-left'
|
|
48
|
-
| 'bottom-center'
|
|
49
|
-
| 'bottom-right'
|
|
50
|
-
| 'left-top'
|
|
51
|
-
| 'left-center'
|
|
52
|
-
| 'left-bottom'
|
|
53
|
-
| 'right-top'
|
|
54
|
-
| 'right-center'
|
|
55
|
-
| 'right-bottom'
|
|
56
|
-
| 'auto';
|
|
41
|
+
position?: PopupPosition;
|
|
57
42
|
margin?: number;
|
|
58
43
|
|
|
59
44
|
// 状態/動作
|
|
@@ -61,7 +46,6 @@
|
|
|
61
46
|
focusTrap?: boolean;
|
|
62
47
|
restoreFocus?: boolean;
|
|
63
48
|
mobileFullscreen?: boolean;
|
|
64
|
-
mobileBehavior?: 'auto' | 'fullscreen' | 'popup';
|
|
65
49
|
enableAutoReposition?: boolean;
|
|
66
50
|
|
|
67
51
|
// ARIA/アクセシビリティ
|
|
@@ -95,8 +79,7 @@
|
|
|
95
79
|
isOpen = $bindable(false),
|
|
96
80
|
focusTrap = false,
|
|
97
81
|
restoreFocus = false,
|
|
98
|
-
mobileFullscreen =
|
|
99
|
-
mobileBehavior = 'auto',
|
|
82
|
+
mobileFullscreen = true,
|
|
100
83
|
enableAutoReposition = true,
|
|
101
84
|
|
|
102
85
|
// ARIA/アクセシビリティ
|
|
@@ -110,7 +93,6 @@
|
|
|
110
93
|
}: PopupProps = $props();
|
|
111
94
|
|
|
112
95
|
let popupRef: HTMLDivElement | undefined = $state();
|
|
113
|
-
let popupId: string = $state(id || `popup-${Math.random().toString(36).substring(2, 15)}`);
|
|
114
96
|
let previousActiveElement: HTMLElement | null = null;
|
|
115
97
|
let isMobile: boolean = $state(false);
|
|
116
98
|
let shouldUseFullscreen: boolean = $state(false);
|
|
@@ -121,20 +103,15 @@
|
|
|
121
103
|
// =========================================================================
|
|
122
104
|
onMount(() => {
|
|
123
105
|
isMobile = isMobileDevice();
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
shouldUseFullscreen = isMobile;
|
|
127
|
-
} else if (mobileBehavior === 'fullscreen') {
|
|
128
|
-
shouldUseFullscreen = true;
|
|
129
|
-
} else {
|
|
130
|
-
shouldUseFullscreen = mobileFullscreen;
|
|
131
|
-
}
|
|
106
|
+
// モバイルの場合のみ fullscreen にするかどうか
|
|
107
|
+
shouldUseFullscreen = isMobile && mobileFullscreen;
|
|
132
108
|
});
|
|
133
109
|
|
|
134
110
|
onDestroy(() => {
|
|
135
111
|
removeEventListenersToClose();
|
|
136
112
|
removeKeyboardListener();
|
|
137
113
|
cleanupMobileFeatures();
|
|
114
|
+
popupManager.unregister(close);
|
|
138
115
|
});
|
|
139
116
|
|
|
140
117
|
// =========================================================================
|
|
@@ -458,18 +435,44 @@
|
|
|
458
435
|
};
|
|
459
436
|
|
|
460
437
|
export const open = async () => {
|
|
438
|
+
// 他の開いているPopupをすべて閉じる
|
|
439
|
+
popupManager.closeOthers(close);
|
|
440
|
+
|
|
461
441
|
previousActiveElement = document.activeElement as HTMLElement;
|
|
462
442
|
|
|
463
443
|
setTimeout(async () => {
|
|
464
444
|
popupRef?.removeEventListener('animationend', closeEnd);
|
|
445
|
+
|
|
446
|
+
if (shouldUseFullscreen && popupRef) {
|
|
447
|
+
// ボトムシート: showPopover()の前に位置を画面下部に設定
|
|
448
|
+
popupRef.style.position = 'fixed';
|
|
449
|
+
popupRef.style.top = 'auto';
|
|
450
|
+
popupRef.style.bottom = '0';
|
|
451
|
+
popupRef.style.left = '0';
|
|
452
|
+
popupRef.style.right = '0';
|
|
453
|
+
popupRef.style.width = '100%';
|
|
454
|
+
popupRef.style.margin = '0';
|
|
455
|
+
}
|
|
456
|
+
|
|
465
457
|
popupRef?.showPopover();
|
|
466
458
|
isOpen = true;
|
|
467
459
|
addEventListenersToClose();
|
|
468
460
|
addKeyboardListener();
|
|
469
461
|
|
|
462
|
+
popupManager.register(close);
|
|
463
|
+
|
|
470
464
|
await tick();
|
|
471
465
|
|
|
472
|
-
if (
|
|
466
|
+
if (shouldUseFullscreen && popupRef) {
|
|
467
|
+
// ボトムシート: 位置を再確認・再設定(ブラウザが上書きする可能性があるため)
|
|
468
|
+
popupRef.style.position = 'fixed';
|
|
469
|
+
popupRef.style.top = 'auto';
|
|
470
|
+
popupRef.style.bottom = '0';
|
|
471
|
+
popupRef.style.left = '0';
|
|
472
|
+
popupRef.style.right = '0';
|
|
473
|
+
popupRef.style.width = '100%';
|
|
474
|
+
popupRef.style.margin = '0';
|
|
475
|
+
} else {
|
|
473
476
|
setPosition();
|
|
474
477
|
await new Promise((resolve) => requestAnimationFrame(resolve));
|
|
475
478
|
setPosition();
|
|
@@ -491,6 +494,8 @@
|
|
|
491
494
|
removeEventListenersToClose();
|
|
492
495
|
removeKeyboardListener();
|
|
493
496
|
cleanupMobileFeatures();
|
|
497
|
+
popupManager.unregister(close);
|
|
498
|
+
// アニメーション完了後に閉じる(fullscreen/通常共通)
|
|
494
499
|
popupRef?.addEventListener('animationend', closeEnd, { once: true });
|
|
495
500
|
// onCloseはアニメーション完了後に呼ぶ(closeEndで実行)
|
|
496
501
|
};
|
|
@@ -506,6 +511,12 @@
|
|
|
506
511
|
export const getIsOpen = () => {
|
|
507
512
|
return isOpen;
|
|
508
513
|
};
|
|
514
|
+
|
|
515
|
+
// =========================================================================
|
|
516
|
+
// $derived
|
|
517
|
+
// =========================================================================
|
|
518
|
+
const generatedPopupId = $state(`popup-${Math.random().toString(36).substring(2, 15)}`);
|
|
519
|
+
const popupId = $derived(id || generatedPopupId);
|
|
509
520
|
</script>
|
|
510
521
|
|
|
511
522
|
<div
|
|
@@ -513,7 +524,6 @@
|
|
|
513
524
|
bind:this={popupRef}
|
|
514
525
|
class="popup"
|
|
515
526
|
class:popup--fade-out={!isOpen}
|
|
516
|
-
class:popup--mobile={isMobile}
|
|
517
527
|
class:popup--fullscreen={shouldUseFullscreen}
|
|
518
528
|
{role}
|
|
519
529
|
aria-label={ariaLabel}
|
|
@@ -525,15 +535,7 @@
|
|
|
525
535
|
close();
|
|
526
536
|
}}
|
|
527
537
|
>
|
|
528
|
-
{
|
|
529
|
-
<div class="popup__mobile">
|
|
530
|
-
<div class="popup__mobile-content">
|
|
531
|
-
{@render children()}
|
|
532
|
-
</div>
|
|
533
|
-
</div>
|
|
534
|
-
{:else}
|
|
535
|
-
{@render children()}
|
|
536
|
-
{/if}
|
|
538
|
+
{@render children()}
|
|
537
539
|
</div>
|
|
538
540
|
|
|
539
541
|
<style>@charset "UTF-8";
|
|
@@ -581,46 +583,39 @@
|
|
|
581
583
|
}
|
|
582
584
|
|
|
583
585
|
/* =============================================
|
|
584
|
-
*
|
|
586
|
+
* Fullscreen variant (Bottom Sheet)
|
|
585
587
|
* ============================================= */
|
|
586
|
-
|
|
587
|
-
position: fixed;
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
588
|
+
.popup--fullscreen {
|
|
589
|
+
position: fixed !important;
|
|
590
|
+
top: auto !important;
|
|
591
|
+
bottom: 0 !important;
|
|
592
|
+
left: 0 !important;
|
|
593
|
+
right: 0 !important;
|
|
594
|
+
width: 100% !important;
|
|
595
|
+
max-height: 90vh;
|
|
596
|
+
margin: 0 !important;
|
|
592
597
|
border: none;
|
|
593
|
-
border-radius:
|
|
594
|
-
|
|
595
|
-
|
|
598
|
+
border-top-left-radius: var(--svelte-ui-popup-mobile-border-radius);
|
|
599
|
+
border-top-right-radius: var(--svelte-ui-popup-mobile-border-radius);
|
|
600
|
+
border-bottom-left-radius: 0;
|
|
601
|
+
border-bottom-right-radius: 0;
|
|
602
|
+
box-shadow: 0 -4px 6px -1px rgba(0, 0, 0, 0.1), 0 -2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
603
|
+
background: var(--svelte-ui-surface-color);
|
|
596
604
|
z-index: var(--svelte-ui-z-modal);
|
|
605
|
+
overflow-y: auto;
|
|
606
|
+
overflow-x: hidden;
|
|
607
|
+
padding-bottom: env(safe-area-inset-bottom, 0px);
|
|
597
608
|
}
|
|
598
609
|
|
|
599
|
-
:popover-open.popup--
|
|
600
|
-
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
.popup__mobile {
|
|
604
|
-
position: absolute;
|
|
605
|
-
bottom: 0;
|
|
606
|
-
left: 0;
|
|
607
|
-
right: 0;
|
|
608
|
-
background: var(--svelte-ui-surface-color);
|
|
609
|
-
border-top-left-radius: var(--svelte-ui-popup-mobile-border-radius);
|
|
610
|
-
border-top-right-radius: var(--svelte-ui-popup-mobile-border-radius);
|
|
611
|
-
max-height: 90vh;
|
|
612
|
-
overflow: hidden;
|
|
610
|
+
:popover-open.popup--fullscreen {
|
|
611
|
+
transform: translateY(0);
|
|
613
612
|
animation: slideUpMobile 300ms ease-out;
|
|
614
|
-
box-shadow: 0 -4px 6px -1px rgba(0, 0, 0, 0.1), 0 -2px 4px -1px rgba(0, 0, 0, 0.06);
|
|
615
613
|
}
|
|
616
614
|
|
|
617
|
-
.
|
|
618
|
-
|
|
619
|
-
max-height: calc(90vh - 60px);
|
|
620
|
-
overflow-y: auto;
|
|
615
|
+
:popover-open.popup--fullscreen.popup--fade-out {
|
|
616
|
+
animation: slideDownMobile 300ms ease-in;
|
|
621
617
|
}
|
|
622
618
|
|
|
623
|
-
/* Mobile animations */
|
|
624
619
|
@keyframes slideUpMobile {
|
|
625
620
|
from {
|
|
626
621
|
transform: translateY(100%);
|
|
@@ -629,10 +624,6 @@
|
|
|
629
624
|
transform: translateY(0);
|
|
630
625
|
}
|
|
631
626
|
}
|
|
632
|
-
:popover-open.popup--mobile.popup--fullscreen.popup--fade-out .popup__mobile {
|
|
633
|
-
animation: slideDownMobile 300ms ease-in;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
627
|
@keyframes slideDownMobile {
|
|
637
628
|
from {
|
|
638
629
|
transform: translateY(0);
|
|
@@ -641,14 +632,10 @@
|
|
|
641
632
|
transform: translateY(100%);
|
|
642
633
|
}
|
|
643
634
|
}
|
|
644
|
-
/* Responsive design adjustments */
|
|
645
635
|
@media (max-width: 480px) {
|
|
646
|
-
.
|
|
647
|
-
border-radius: 0;
|
|
636
|
+
:popover-open.popup--fullscreen {
|
|
648
637
|
max-height: 100vh;
|
|
649
|
-
|
|
650
|
-
.popup__mobile-content {
|
|
651
|
-
max-height: calc(100vh - 60px);
|
|
638
|
+
border-radius: 0;
|
|
652
639
|
}
|
|
653
640
|
}
|
|
654
641
|
/* Reduced motion support */
|