@14ch/svelte-ui 0.0.8 → 0.0.10

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.
@@ -684,18 +684,24 @@
684
684
  --svelte-ui-snackbar-error-outlined-border-color: var(--svelte-ui-error-color);
685
685
 
686
686
  /* Skeleton */
687
+ --svelte-ui-skeleton-box-height: 240px;
687
688
  --svelte-ui-skeleton-box-border-radius: var(--svelte-ui-border-radius);
688
- --svelte-ui-skeleton-text-border-radius: var(--svelte-ui-border-radius);
689
+ --svelte-ui-skeleton-text-border-radius: var(--svelte-ui-border-radius-rounded);
690
+ --svelte-ui-skeleton-text-line-height: 1em;
689
691
  --svelte-ui-skeleton-avatar-image-border-radius: var(--svelte-ui-border-radius-rounded);
690
- --svelte-ui-skeleton-image-border-radius: var(--svelte-ui-border-radius);
692
+ --svelte-ui-skeleton-avatar-image-size: 48px;
693
+ --svelte-ui-skeleton-avatar-text-width: 160px;
691
694
  --svelte-ui-skeleton-button-border-radius: var(--svelte-ui-border-radius);
692
- --svelte-ui-skeleton-bg-color: var(--svelte-ui-skeleton-color);
693
- --svelte-ui-skeleton-text-bg-color: var(--svelte-ui-skeleton-color);
694
- --svelte-ui-skeleton-image-bg-color: var(--svelte-ui-skeleton-color);
695
- --svelte-ui-skeleton-button-bg-color: var(--svelte-ui-skeleton-color);
696
- --svelte-ui-skeleton-input-bg-color: var(--svelte-ui-skeleton-color);
697
- --svelte-ui-skeleton-textarea-bg-color: var(--svelte-ui-skeleton-color);
698
- --svelte-ui-skeleton-avatar-bg-color: var(--svelte-ui-skeleton-color);
695
+ --svelte-ui-skeleton-button-width: 120px;
696
+ --svelte-ui-skeleton-button-height: 36px;
697
+ --svelte-ui-skeleton-media-thumbnail-border-radius: var(--svelte-ui-border-radius);
698
+ --svelte-ui-skeleton-media-thumbnail-width: 160px;
699
+ --svelte-ui-skeleton-media-gap: 16px;
700
+ --svelte-ui-skeleton-media-vertical-gap: 8px;
701
+ --svelte-ui-skeleton-heading-width: 50%;
702
+ --svelte-ui-skeleton-heading-font-size: 2rem;
703
+ --svelte-ui-skeleton-pattern-gap: 16px;
704
+ --svelte-ui-skeleton-repeat-gap: 24px;
699
705
 
700
706
  /* Badge */
701
707
  --svelte-ui-badge-font-size: var(--svelte-ui-font-size-sm);
@@ -821,12 +827,5 @@
821
827
  /* ===== スケルトンの上書き ===== */
822
828
  --svelte-ui-skeleton-color: CanvasText;
823
829
  --svelte-ui-skeleton-shimmer-color: Highlight;
824
- --svelte-ui-skeleton-bg-color: CanvasText;
825
- --svelte-ui-skeleton-text-bg-color: CanvasText;
826
- --svelte-ui-skeleton-image-bg-color: CanvasText;
827
- --svelte-ui-skeleton-button-bg-color: CanvasText;
828
- --svelte-ui-skeleton-input-bg-color: CanvasText;
829
- --svelte-ui-skeleton-textarea-bg-color: CanvasText;
830
- --svelte-ui-skeleton-avatar-bg-color: CanvasText;
831
830
  }
832
831
  }
@@ -92,8 +92,8 @@
92
92
  iconVariant?: IconVariant;
93
93
 
94
94
  // 入力イベント
95
- onchange?: (value: any) => void;
96
- oninput?: (value: any) => void;
95
+ onchange?: (value: string) => void;
96
+ oninput?: (value: string) => void;
97
97
 
98
98
  // フォーカスイベント
99
99
  onfocus?: Function; // No params for type inference
@@ -148,7 +148,7 @@
148
148
  localValue = value;
149
149
 
150
150
  /* value が更新されたらonchangeを実行 */
151
- handleChange();
151
+ handleValueChange();
152
152
  });
153
153
  });
154
154
 
@@ -167,10 +167,11 @@
167
167
  // Methods
168
168
  // =========================================================================
169
169
 
170
- const handleChange = (event?: Event): void => {
171
- // 空文字列の場合はそのまま処理
172
- if (localValue && !localValue.startsWith('#')) {
173
- localValue = '#' + localValue;
170
+ const handleChange = (value: string): void => {
171
+ if (value && !value.startsWith('#')) {
172
+ localValue = '#' + value;
173
+ } else {
174
+ localValue = value;
174
175
  }
175
176
 
176
177
  if (value !== prevValue || localValue !== prevValue) {
@@ -180,9 +181,13 @@
180
181
  }
181
182
  };
182
183
 
183
- const handleInput = (event?: Event): void => {
184
+ const handleValueChange = (): void => {
185
+ handleChange(localValue);
186
+ };
187
+
188
+ const handleInput = (inputValue: string | number): void => {
184
189
  if (disabled) return;
185
- oninput?.(localValue);
190
+ oninput?.(String(inputValue));
186
191
  };
187
192
 
188
193
  const handleFocus = (event: FocusEvent): void => {
@@ -205,7 +210,7 @@
205
210
  // Enterキーで色の変更を確定
206
211
  if (event.key === 'Enter' && !disabled && !readonly) {
207
212
  event.preventDefault();
208
- handleChange();
213
+ handleValueChange();
209
214
  }
210
215
  if (disabled) return;
211
216
  onkeydown(event);
@@ -368,7 +373,7 @@
368
373
  <input
369
374
  type="color"
370
375
  bind:value
371
- onchange={handleChange}
376
+ onchange={handleValueChange}
372
377
  onfocus={handleFocus}
373
378
  onblur={handleBlur}
374
379
  onclick={handleClick}
@@ -13,8 +13,8 @@ type $$ComponentProps = {
13
13
  clearable?: boolean;
14
14
  clearButtonAriaLabel?: string;
15
15
  iconVariant?: IconVariant;
16
- onchange?: (value: any) => void;
17
- oninput?: (value: any) => void;
16
+ onchange?: (value: string) => void;
17
+ oninput?: (value: string) => void;
18
18
  onfocus?: Function;
19
19
  onblur?: Function;
20
20
  onkeydown?: Function;
@@ -108,8 +108,8 @@
108
108
  clearable?: boolean;
109
109
 
110
110
  // 入力イベント
111
- onchange?: (value: any) => void;
112
- oninput?: (value: any) => void;
111
+ onchange?: (value: string | number | null | undefined) => void;
112
+ oninput?: (value: string | number | null | undefined) => void;
113
113
 
114
114
  // フォーカスイベント
115
115
  onfocus?: Function; // No params for type inference
@@ -206,7 +206,7 @@
206
206
  // 入力中はvalueを更新しない(入力値をそのまま保持)
207
207
  highlightedIndex = -1;
208
208
  popupRef?.open();
209
- oninput?.(currentInputValue);
209
+ oninput?.(currentValue ?? null);
210
210
  };
211
211
 
212
212
  // 値確定ハンドラー
@@ -20,8 +20,8 @@ type $$ComponentProps = {
20
20
  required?: boolean;
21
21
  filterable?: boolean;
22
22
  clearable?: boolean;
23
- onchange?: (value: any) => void;
24
- oninput?: (value: any) => void;
23
+ onchange?: (value: string | number | null | undefined) => void;
24
+ oninput?: (value: string | number | null | undefined) => void;
25
25
  onfocus?: Function;
26
26
  onblur?: Function;
27
27
  onkeydown?: Function;
@@ -13,7 +13,7 @@
13
13
  // 基本プロパティ
14
14
  title = 'Confirm',
15
15
  description = 'Are you sure?',
16
- confirmLabel = 'Confirm',
16
+ submitLabel = 'Confirm',
17
17
  cancelLabel = 'Cancel',
18
18
 
19
19
  // HTML属性
@@ -28,13 +28,13 @@
28
28
  closeIfClickOutside = true,
29
29
 
30
30
  // イベントハンドラー
31
- onConfirm = () => {}, // No params for type inference
31
+ onSubmit = () => {}, // No params for type inference
32
32
  onCancel = () => {} // No params for type inference
33
33
  }: {
34
34
  // 基本プロパティ
35
35
  title?: string;
36
36
  description?: string;
37
- confirmLabel?: string;
37
+ submitLabel?: string;
38
38
  cancelLabel?: string;
39
39
 
40
40
  // HTML属性
@@ -49,7 +49,7 @@
49
49
  closeIfClickOutside?: boolean;
50
50
 
51
51
  // イベントハンドラー
52
- onConfirm?: () => void;
52
+ onSubmit?: () => void;
53
53
  onCancel?: () => void;
54
54
  } = $props();
55
55
 
@@ -58,8 +58,8 @@
58
58
  // =========================================================================
59
59
  // Methods
60
60
  // =========================================================================
61
- const handleConfirm = (): void => {
62
- onConfirm();
61
+ const handleSubmit = (): void => {
62
+ onSubmit();
63
63
  close();
64
64
  };
65
65
 
@@ -104,9 +104,9 @@
104
104
  : variant === 'warning'
105
105
  ? 'var(--svelte-ui-warning-color)'
106
106
  : undefined}
107
- onclick={handleConfirm}
107
+ onclick={handleSubmit}
108
108
  >
109
- {confirmLabel}
109
+ {submitLabel}
110
110
  </Button>
111
111
  {/snippet}
112
112
  </Dialog>
@@ -1,14 +1,14 @@
1
1
  type $$ComponentProps = {
2
2
  title?: string;
3
3
  description?: string;
4
- confirmLabel?: string;
4
+ submitLabel?: string;
5
5
  cancelLabel?: string;
6
6
  id?: string;
7
7
  variant?: 'info' | 'warning' | 'danger';
8
8
  width?: string | number;
9
9
  isOpen?: boolean;
10
10
  closeIfClickOutside?: boolean;
11
- onConfirm?: () => void;
11
+ onSubmit?: () => void;
12
12
  onCancel?: () => void;
13
13
  };
14
14
  declare const ConfirmDialog: import("svelte").Component<$$ComponentProps, {
@@ -53,7 +53,7 @@
53
53
  mode?: 'single' | 'range';
54
54
 
55
55
  // 入力イベント
56
- onchange: (value: Date | { start: Date; end: Date } | undefined) => void;
56
+ onchange?: (value: Date | { start: Date; end: Date } | undefined) => void;
57
57
  onOpen?: Function;
58
58
  onClose?: Function;
59
59
  } = $props();
@@ -366,12 +366,23 @@
366
366
  }
367
367
  };
368
368
 
369
+ const getNormalizedRange = () => {
370
+ if (mode !== 'range' || !value || !('start' in value && 'end' in value)) return null;
371
+
372
+ const startDate = dayjs(value.start).startOf('day');
373
+ const endDate = dayjs(value.end).startOf('day');
374
+
375
+ if (startDate.isSameOrBefore(endDate)) {
376
+ return { start: startDate, end: endDate };
377
+ }
378
+
379
+ return { start: endDate, end: startDate };
380
+ };
381
+
369
382
  const isSelected = (date: dayjs.Dayjs) => {
370
- if (mode === 'range' && value && 'start' in value && 'end' in value) {
371
- return (
372
- dayjs(date).isSameOrAfter(dayjs(value.start).startOf('day')) &&
373
- dayjs(date).isSameOrBefore(dayjs(value.end).startOf('day'))
374
- );
383
+ const range = getNormalizedRange();
384
+ if (range) {
385
+ return dayjs(date).isSameOrAfter(range.start) && dayjs(date).isSameOrBefore(range.end);
375
386
  } else if (mode === 'single' && value && value instanceof Date) {
376
387
  return dayjs(date).isSame(dayjs(value).startOf('day'));
377
388
  }
@@ -379,25 +390,29 @@
379
390
  };
380
391
 
381
392
  const isRangeStart = (date: dayjs.Dayjs) => {
382
- if (mode !== 'range' || !value || !('start' in value && 'end' in value)) return false;
393
+ const range = getNormalizedRange();
394
+ if (!range) return false;
383
395
  if (isRangePreviewActive) return false;
384
- return dayjs(date).isSame(dayjs(value.start).startOf('day'));
396
+ return dayjs(date).isSame(range.start);
385
397
  };
386
398
 
387
399
  const isRangeEnd = (date: dayjs.Dayjs) => {
388
- if (mode !== 'range' || !value || !('start' in value && 'end' in value)) return false;
400
+ const range = getNormalizedRange();
401
+ if (!range) return false;
389
402
  if (isRangePreviewActive) return false;
390
- return dayjs(date).isSame(dayjs(value.end).startOf('day'));
403
+ return dayjs(date).isSame(range.end);
391
404
  };
392
405
 
393
406
  const isRangeMiddle = (date: dayjs.Dayjs) => {
394
- if (mode !== 'range' || !value || !('start' in value && 'end' in value)) return false;
407
+ const range = getNormalizedRange();
408
+ if (!range) return false;
395
409
  if (isRangePreviewActive) return false;
396
410
  return isSelected(date) && !isRangeStart(date) && !isRangeEnd(date);
397
411
  };
398
412
 
399
413
  const isRangeSingle = (date: dayjs.Dayjs) => {
400
- if (mode !== 'range' || !value || !('start' in value && 'end' in value)) return false;
414
+ const range = getNormalizedRange();
415
+ if (!range) return false;
401
416
  if (isRangePreviewActive) return false;
402
417
  return isRangeStart(date) && isRangeEnd(date);
403
418
  };
@@ -14,7 +14,7 @@ type $$ComponentProps = {
14
14
  maxDate?: Date;
15
15
  id?: string;
16
16
  mode?: 'single' | 'range';
17
- onchange: (value: Date | {
17
+ onchange?: (value: Date | {
18
18
  start: Date;
19
19
  end: Date;
20
20
  } | undefined) => void;
@@ -7,6 +7,7 @@
7
7
  import type { HTMLInputAttributes } from 'svelte/elements';
8
8
  import type { IconVariant, IconWeight, IconGrade, IconOpticalSize } from '../types/icon';
9
9
  import { t } from '../i18n';
10
+ import { convertToHtmlWithLink } from '../utils/formatText';
10
11
 
11
12
  // =========================================================================
12
13
  // Props, States & Constants
@@ -58,6 +59,7 @@
58
59
  required = false,
59
60
  clearable = false,
60
61
  clearButtonAriaLabel = t('input.clear'),
62
+ linkify = false,
61
63
 
62
64
  // フォーカスイベント
63
65
  onfocus = () => {}, // No params for type inference
@@ -148,6 +150,7 @@
148
150
  readonly?: boolean;
149
151
  required?: boolean;
150
152
  clearable?: boolean;
153
+ linkify?: boolean;
151
154
 
152
155
  // フォーカスイベント
153
156
  onfocus?: Function; // No params for type inference
@@ -183,8 +186,8 @@
183
186
  onpointercancel?: Function; // No params for type inference
184
187
 
185
188
  // 入力イベント
186
- onchange?: (value: any) => void;
187
- oninput?: (value: any) => void;
189
+ onchange?: (value: string | number) => void;
190
+ oninput?: (value: string | number) => void;
188
191
 
189
192
  // アイコンイベント
190
193
  onRightIconClick?: Function; // No params for type inference
@@ -374,6 +377,12 @@
374
377
  return String(value);
375
378
  };
376
379
 
380
+ const linkHtmlValue = $derived.by(() => {
381
+ if (!linkify) return '';
382
+ const result = convertToHtmlWithLink(getDisplayValue());
383
+ return typeof result === 'string' ? result : String(result ?? '');
384
+ });
385
+
377
386
  const widthStyle = $derived(getStyleFromNumber(width));
378
387
  const maxWidthStyle = $derived(getStyleFromNumber(maxWidth));
379
388
  const minWidthStyle = $derived(getStyleFromNumber(minWidth));
@@ -384,6 +393,7 @@
384
393
  input--focus-{focusStyle}
385
394
  input--type-{type}"
386
395
  class:input--inline={inline}
396
+ class:input--linkify={linkify}
387
397
  class:input--auto-resize={inline}
388
398
  class:input--full-width={fullWidth}
389
399
  class:input--clearable={clearable}
@@ -456,6 +466,11 @@
456
466
  {...restProps}
457
467
  />
458
468
  </div>
469
+ {#if linkify}
470
+ <div class="input__link-text" style={customStyle}>
471
+ {@html linkHtmlValue}
472
+ </div>
473
+ {/if}
459
474
  <!-- クリアボタン -->
460
475
  {#if clearable && !disabled && !readonly}
461
476
  <div class="input__clear-button">
@@ -613,6 +628,35 @@
613
628
  }
614
629
  }
615
630
 
631
+ /* リンク表示用オーバーレイ */
632
+ .input__link-text {
633
+ position: absolute;
634
+ top: 0;
635
+ left: 0;
636
+ width: 100%;
637
+ height: 100%;
638
+ display: flex;
639
+ align-items: center;
640
+ padding: inherit;
641
+ background: transparent;
642
+ border-radius: inherit;
643
+ font-size: inherit;
644
+ font-weight: inherit;
645
+ color: inherit;
646
+ line-height: inherit;
647
+ text-align: inherit;
648
+ white-space: nowrap;
649
+ overflow: hidden;
650
+ text-overflow: ellipsis;
651
+ pointer-events: none;
652
+ z-index: 1;
653
+ }
654
+
655
+ .input__link-text :global(a) {
656
+ pointer-events: auto;
657
+ text-decoration: underline;
658
+ }
659
+
616
660
  .input__clear-button {
617
661
  position: absolute;
618
662
  top: 50%;
@@ -695,7 +739,6 @@
695
739
 
696
740
  input {
697
741
  min-height: var(--svelte-ui-input-height);
698
- padding: var(--svelte-ui-input-padding);
699
742
  background-color: var(--svelte-ui-input-bg);
700
743
  box-shadow: 0 0 0 var(--svelte-ui-border-width) inset var(--svelte-ui-input-border-color);
701
744
  border: none;
@@ -703,30 +746,40 @@
703
746
  color: var(--svelte-ui-input-text-color);
704
747
  }
705
748
 
749
+ input,
750
+ .input__display-text,
751
+ .input__link-text {
752
+ padding: var(--svelte-ui-input-padding);
753
+ }
754
+
706
755
  &.input--has-left-icon {
707
756
  input,
708
- .input__display-text {
757
+ .input__display-text,
758
+ .input__link-text {
709
759
  padding-left: var(--svelte-ui-input-icon-space);
710
760
  }
711
761
  }
712
762
 
713
763
  &.input--has-right-icon {
714
764
  input,
715
- .input__display-text {
765
+ .input__display-text,
766
+ .input__link-text {
716
767
  padding-right: var(--svelte-ui-input-icon-space);
717
768
  }
718
769
  }
719
770
 
720
771
  &.input--clearable {
721
772
  input,
722
- .input__display-text {
773
+ .input__display-text,
774
+ .input__link-text {
723
775
  padding-right: var(--svelte-ui-input-icon-space);
724
776
  }
725
777
  }
726
778
 
727
779
  &.input--clearable.input--has-right-icon {
728
780
  input,
729
- .input__display-text {
781
+ .input__display-text,
782
+ .input__link-text {
730
783
  padding-right: var(--svelte-ui-input-icon-space-double);
731
784
  }
732
785
  }
@@ -741,6 +794,18 @@
741
794
  }
742
795
  }
743
796
 
797
+ /* linkify=true かつフォーカスがないときは、input のテキストカラーだけ透明にして二重描画を防ぐ */
798
+ .input--linkify:not(.input--focused) input {
799
+ color: transparent;
800
+ caret-color: transparent;
801
+ text-shadow: none;
802
+ }
803
+
804
+ /* フォーカス時はリンク用オーバーレイも非表示にして(display:none)、リンクが反応しないようにする */
805
+ .input--focused .input__link-text {
806
+ display: none;
807
+ }
808
+
744
809
  /* =============================================
745
810
  * デザインバリアント:inline
746
811
  * ============================================= */
@@ -759,28 +824,32 @@
759
824
 
760
825
  &.input--has-left-icon {
761
826
  input,
762
- .input__display-text {
827
+ .input__display-text,
828
+ .input__link-text {
763
829
  padding-left: var(--svelte-ui-input-icon-space-inline);
764
830
  }
765
831
  }
766
832
 
767
833
  &.input--has-right-icon {
768
834
  input,
769
- .input__display-text {
835
+ .input__display-text,
836
+ .input__link-text {
770
837
  padding-right: var(--svelte-ui-input-icon-space-inline);
771
838
  }
772
839
  }
773
840
 
774
841
  &.input--clearable {
775
842
  input,
776
- .input__display-text {
843
+ .input__display-text,
844
+ .input__link-text {
777
845
  padding-right: var(--svelte-ui-input-icon-space-inline);
778
846
  }
779
847
  }
780
848
 
781
849
  &.input--clearable.input--has-right-icon {
782
850
  input,
783
- .input__display-text {
851
+ .input__display-text,
852
+ .input__link-text {
784
853
  padding-right: var(--svelte-ui-input-icon-space-double-inline);
785
854
  }
786
855
  }
@@ -796,6 +865,15 @@
796
865
  }
797
866
  }
798
867
 
868
+ /* inline + linkify のときは、display-text を常に隠し、wrapper を常に表示 */
869
+ .input--inline.input--linkify .input__display-text {
870
+ opacity: 0;
871
+ }
872
+
873
+ .input--inline.input--linkify .input__wrapper {
874
+ opacity: 1;
875
+ }
876
+
799
877
  /* =============================================
800
878
  * レイアウトバリエーション
801
879
  * ============================================= */
@@ -812,21 +890,24 @@
812
890
  * ============================================= */
813
891
  .input--clearable {
814
892
  input,
815
- .input__display-text {
893
+ .input__display-text,
894
+ .input__link-text {
816
895
  padding-right: var(--svelte-ui-input-icon-space);
817
896
  }
818
897
  }
819
898
 
820
899
  .input.input--has-right-icon {
821
900
  input,
822
- .input__display-text {
901
+ .input__display-text,
902
+ .input__link-text {
823
903
  padding-right: var(--svelte-ui-input-icon-space);
824
904
  }
825
905
  }
826
906
 
827
907
  .input.input--has-left-icon {
828
908
  input,
829
- .input__display-text {
909
+ .input__display-text,
910
+ .input__link-text {
830
911
  padding-left: var(--svelte-ui-input-icon-space);
831
912
  }
832
913
  }
@@ -37,6 +37,7 @@ type $$ComponentProps = {
37
37
  readonly?: boolean;
38
38
  required?: boolean;
39
39
  clearable?: boolean;
40
+ linkify?: boolean;
40
41
  onfocus?: Function;
41
42
  onblur?: Function;
42
43
  onkeydown?: Function;
@@ -60,8 +61,8 @@ type $$ComponentProps = {
60
61
  onpointerleave?: Function;
61
62
  onpointermove?: Function;
62
63
  onpointercancel?: Function;
63
- onchange?: (value: any) => void;
64
- oninput?: (value: any) => void;
64
+ onchange?: (value: string | number) => void;
65
+ oninput?: (value: string | number) => void;
65
66
  onRightIconClick?: Function;
66
67
  onLeftIconClick?: Function;
67
68
  [key: string]: any;
@@ -92,7 +92,7 @@
92
92
  reducedMotion?: boolean;
93
93
 
94
94
  // 入力イベント
95
- onchange?: (value: any) => void;
95
+ onchange?: (value: string | number | boolean) => void;
96
96
 
97
97
  // フォーカスイベント
98
98
  onfocus?: Function; // No params for type inference
@@ -11,7 +11,7 @@ type $$ComponentProps = {
11
11
  disabled?: boolean;
12
12
  required?: boolean;
13
13
  reducedMotion?: boolean;
14
- onchange?: (value: any) => void;
14
+ onchange?: (value: string | number | boolean) => void;
15
15
  onfocus?: Function;
16
16
  onblur?: Function;
17
17
  onkeydown?: Function;
@@ -128,7 +128,7 @@
128
128
  onpointercancel?: Function; // No params for type inference
129
129
 
130
130
  // 入力イベント
131
- onchange?: (value: any) => void;
131
+ onchange?: (value: string | number | null | undefined) => void;
132
132
 
133
133
  // その他
134
134
  [key: string]: any;
@@ -39,7 +39,7 @@ type $$ComponentProps = {
39
39
  onpointerleave?: Function;
40
40
  onpointermove?: Function;
41
41
  onpointercancel?: Function;
42
- onchange?: (value: any) => void;
42
+ onchange?: (value: string | number | null | undefined) => void;
43
43
  [key: string]: any;
44
44
  };
45
45
  declare const Select: import("svelte").Component<$$ComponentProps, {}, "value">;
@@ -126,8 +126,8 @@
126
126
  onpointercancel?: Function; // No params for type inference
127
127
 
128
128
  // 入力イベント
129
- onchange?: Function; // No params for type inference
130
- oninput?: Function; // No params for type inference
129
+ onchange?: (value: number) => void;
130
+ oninput?: (value: number) => void;
131
131
 
132
132
  // その他
133
133
  [key: string]: any;
@@ -37,8 +37,8 @@ type $$ComponentProps = {
37
37
  onpointerleave?: Function;
38
38
  onpointermove?: Function;
39
39
  onpointercancel?: Function;
40
- onchange?: Function;
41
- oninput?: Function;
40
+ onchange?: (value: number) => void;
41
+ oninput?: (value: number) => void;
42
42
  [key: string]: any;
43
43
  };
44
44
  declare const Slider: import("svelte").Component<$$ComponentProps, {}, "value">;
@@ -4,6 +4,7 @@
4
4
  import IconButton from './IconButton.svelte';
5
5
  import { getStyleFromNumber } from '../utils/style';
6
6
  import { t } from '../i18n';
7
+ import { convertToHtml, convertToHtmlWithLink } from '../utils/formatText';
7
8
  import type { HTMLTextareaAttributes } from 'svelte/elements';
8
9
  import type { IconVariant } from '../types/icon';
9
10
 
@@ -29,7 +30,7 @@
29
30
 
30
31
  // スタイル/レイアウト
31
32
  rows = 3,
32
- minHeight = 36,
33
+ minHeight = null,
33
34
  maxHeight = null,
34
35
  inline = false,
35
36
  focusStyle = 'outline',
@@ -48,6 +49,7 @@
48
49
  readonly = false,
49
50
  required = false,
50
51
  iconVariant = 'outlined',
52
+ linkify = false,
51
53
 
52
54
  // フォーカスイベント
53
55
  onfocus = () => {}, // No params for type inference
@@ -125,6 +127,7 @@
125
127
  readonly?: boolean;
126
128
  required?: boolean;
127
129
  iconVariant?: IconVariant;
130
+ linkify?: boolean;
128
131
 
129
132
  // フォーカスイベント
130
133
  onfocus?: Function; // No params for type inference
@@ -160,8 +163,8 @@
160
163
  onpointercancel?: Function; // No params for type inference
161
164
 
162
165
  // 入力イベント
163
- onchange?: (value: any) => void;
164
- oninput?: (value: any) => void;
166
+ onchange?: (value: string) => void;
167
+ oninput?: (value: string) => void;
165
168
 
166
169
  // その他
167
170
  [key: string]: any;
@@ -324,9 +327,11 @@
324
327
  // $derived
325
328
  // =========================================================================
326
329
 
327
- // min-heightスタイルの計算
328
- const minHeightStyle = $derived(
329
- inline ? 'min-height: 1.6em; min-height: 1lh;' : `min-height: ${minHeight}px;`
330
+ // min-height用CSS変数の上書きスタイル
331
+ // デフォルト値は variables.scss の --svelte-ui-textarea-min-height に委譲し、
332
+ // props minHeight が指定されたときだけ上書きする
333
+ const minHeightVarStyle = $derived(
334
+ !inline && minHeight != null ? `--svelte-ui-textarea-min-height: ${minHeight}px;` : ''
330
335
  );
331
336
 
332
337
  const maxHeightStyle = $derived(getStyleFromNumber(maxHeight));
@@ -335,9 +340,7 @@
335
340
  // HTML表示用の値(autoResize時の高さ調整用)
336
341
  const htmlValue = $derived.by(() => {
337
342
  if (value !== '') {
338
- let html = value
339
- .replace(/ +/g, (match) => '&nbsp;'.repeat(match.length))
340
- .replace(/\n/g, '<br />');
343
+ let html = convertToHtml(value) as string;
341
344
  // 最後の行が空だったら空白を追加(高さ調整のため)
342
345
  const lines = html.split('<br />');
343
346
  if (lines.length > 0 && lines[lines.length - 1] === '') {
@@ -345,8 +348,23 @@
345
348
  }
346
349
  return html;
347
350
  } else {
351
+ // inline かつ value が空のとき、placeholder がなければ
352
+ // 1行分の高さを確保するためにダミーの &nbsp; を入れる
353
+ // (placeholder がある場合は :empty::before でプレースホルダを表示したいので空にしておく)
354
+ if (inline && !placeholder) {
355
+ return '&nbsp;';
356
+ }
357
+ return '';
358
+ }
359
+ });
360
+
361
+ // URLをリンク化した表示用HTML(クリック検出用オーバーレイで使用)
362
+ const linkHtmlValue = $derived.by(() => {
363
+ if (!linkify || value === '') {
348
364
  return '';
349
365
  }
366
+ const result = convertToHtmlWithLink(value);
367
+ return typeof result === 'string' ? result : String(result ?? '');
350
368
  });
351
369
  </script>
352
370
 
@@ -354,6 +372,7 @@
354
372
  class="textarea
355
373
  textarea--focus-{focusStyle}"
356
374
  class:textarea--inline={inline}
375
+ class:textarea--linkify={linkify}
357
376
  class:textarea--full-width={fullWidth}
358
377
  class:textarea--full-height={fullHeight}
359
378
  class:textarea--auto-resize={autoResize}
@@ -369,7 +388,7 @@
369
388
  <div
370
389
  class="textarea__display-text"
371
390
  data-placeholder={placeholder}
372
- style="{minHeightStyle} {customStyle}"
391
+ style="{minHeightVarStyle} {customStyle}"
373
392
  >
374
393
  {@html htmlValue}
375
394
  </div>
@@ -391,7 +410,7 @@
391
410
  {spellcheck}
392
411
  {autocapitalize}
393
412
  class:resizable
394
- style="width: {widthStyle}; {minHeightStyle} {customStyle}"
413
+ style="width: {widthStyle}; {minHeightVarStyle} {customStyle}"
395
414
  onchange={handleChange}
396
415
  oninput={handleInput}
397
416
  onfocus={handleFocus}
@@ -438,6 +457,11 @@
438
457
  </div>
439
458
  {/if}
440
459
  </div>
460
+ {#if linkify}
461
+ <div class="textarea__link-text" style="{minHeightVarStyle} {customStyle}">
462
+ {@html linkHtmlValue}
463
+ </div>
464
+ {/if}
441
465
  </div>
442
466
 
443
467
  <style>
@@ -471,11 +495,11 @@
471
495
  }
472
496
 
473
497
  /* =============================================
474
- * 基本コンポーネント
475
- * ============================================= */
476
- .textarea__display-text {
477
- display: flex;
478
- align-items: start; /* テーブルの他の列に合わせて高さが高くなっているときに、上寄せになるようにするための措置 */
498
+ * 基本コンポーネント
499
+ * ============================================= */
500
+ .textarea__display-text,
501
+ .textarea__link-text {
502
+ display: block;
479
503
  width: 100%;
480
504
  background: inherit;
481
505
  border: inherit;
@@ -498,6 +522,23 @@
498
522
  }
499
523
  }
500
524
 
525
+ /* クリック可能なリンク用オーバーレイ */
526
+ .textarea__link-text {
527
+ position: absolute;
528
+ top: 0;
529
+ left: 0;
530
+ width: 100%;
531
+ height: 100%;
532
+ padding: inherit;
533
+ pointer-events: none;
534
+ z-index: 1;
535
+ }
536
+
537
+ .textarea__link-text :global(a) {
538
+ pointer-events: auto;
539
+ text-decoration: underline;
540
+ }
541
+
501
542
  textarea {
502
543
  position: absolute;
503
544
  top: 0;
@@ -626,8 +667,8 @@
626
667
  }
627
668
 
628
669
  /* =============================================
629
- * 表示切り替え(フォーカス時・非autoResize時)
630
- * ============================================= */
670
+ * 表示切り替え(フォーカス時・非inline)
671
+ * ============================================= */
631
672
  .textarea--focused,
632
673
  .textarea:not(.textarea--inline) {
633
674
  .textarea__display-text {
@@ -639,17 +680,29 @@
639
680
  }
640
681
  }
641
682
 
683
+ /* linkify=true かつ非 inline のときは、display-text は常に非表示(レイアウトだけ保持) */
684
+ .textarea--linkify:not(.textarea--inline) .textarea__display-text {
685
+ opacity: 0;
686
+ }
687
+
688
+ /* フォーカス時はリンク用オーバーレイも非表示にして(display:none)、リンクが反応しないようにする */
689
+ .textarea--focused .textarea__link-text {
690
+ display: none;
691
+ }
692
+
642
693
  /* =============================================
643
694
  * デザインバリアント:default
644
695
  * ============================================= */
645
696
  .textarea:not(.textarea--inline) {
646
- .textarea__display-text {
697
+ .textarea__display-text,
698
+ .textarea__link-text,
699
+ textarea {
700
+ min-height: var(--svelte-ui-textarea-min-height);
647
701
  padding: var(--svelte-ui-textarea-padding);
648
702
  }
649
703
 
650
704
  textarea {
651
705
  position: static;
652
- padding: var(--svelte-ui-textarea-padding);
653
706
  background-color: var(--svelte-ui-textarea-bg);
654
707
  box-shadow: 0 0 0 var(--svelte-ui-border-width) inset var(--svelte-ui-textarea-border-color);
655
708
  border: none;
@@ -664,6 +717,15 @@
664
717
  }
665
718
  }
666
719
 
720
+ /* linkify=true かつフォーカスがないときは、textarea のテキストカラーだけ透明にして二重描画を防ぐ
721
+ * placeholder の色は textarea::placeholder 側で指定しているため、この指定の影響を受けない
722
+ */
723
+ .textarea--linkify:not(.textarea--focused) textarea {
724
+ color: transparent;
725
+ caret-color: transparent;
726
+ text-shadow: none;
727
+ }
728
+
667
729
  /* =============================================
668
730
  * デザインバリアント:rounded
669
731
  * ============================================= */
@@ -699,4 +761,13 @@
699
761
  top: var(--svelte-ui-textarea-icon-top-inline);
700
762
  }
701
763
  }
764
+
765
+ /* inline + linkify のときは、display-text を常に隠し、textarea を常に表示 */
766
+ .textarea--inline.textarea--linkify .textarea__display-text {
767
+ opacity: 0;
768
+ }
769
+
770
+ .textarea--inline.textarea--linkify textarea {
771
+ opacity: 1;
772
+ }
702
773
  </style>
@@ -30,6 +30,7 @@ type $$ComponentProps = {
30
30
  readonly?: boolean;
31
31
  required?: boolean;
32
32
  iconVariant?: IconVariant;
33
+ linkify?: boolean;
33
34
  onfocus?: Function;
34
35
  onblur?: Function;
35
36
  onkeydown?: Function;
@@ -53,8 +54,8 @@ type $$ComponentProps = {
53
54
  onpointerleave?: Function;
54
55
  onpointermove?: Function;
55
56
  onpointercancel?: Function;
56
- onchange?: (value: any) => void;
57
- oninput?: (value: any) => void;
57
+ onchange?: (value: string) => void;
58
+ oninput?: (value: string) => void;
58
59
  [key: string]: any;
59
60
  };
60
61
  declare const Textarea: import("svelte").Component<$$ComponentProps, {
@@ -10,6 +10,7 @@
10
10
  import { getStyleFromNumber } from '../../utils/style';
11
11
  import type { SkeletonPatternConfig, SkeletonPresetConfig } from '../../types/skeleton';
12
12
  import { isPresetPattern, isMediaPattern, isAvatarPattern } from '../../types/skeleton';
13
+ import { DEFAULT_PATTERN_CONFIG, PRESET_PATTERNS } from '../../constants/skeleton';
13
14
 
14
15
  // =========================================================================
15
16
  // Props
@@ -19,8 +20,8 @@
19
20
  // 基本プロパティ
20
21
  patterns = [{ type: 'box' }] as SkeletonPatternConfig[],
21
22
  repeat = 1,
22
- repeatGap = '64px',
23
- itemGap = '24px',
23
+ repeatGap = 'var(--svelte-ui-skeleton-repeat-gap)',
24
+ patternGap = 'var(--svelte-ui-skeleton-pattern-gap)',
24
25
  className = '',
25
26
  customStyle = '',
26
27
  animated = true
@@ -28,77 +29,12 @@
28
29
  patterns?: SkeletonPatternConfig[];
29
30
  repeat?: number;
30
31
  repeatGap?: string | number;
31
- itemGap?: string | number;
32
+ patternGap?: string | number;
32
33
  className?: string;
33
34
  customStyle?: string;
34
35
  animated?: boolean;
35
36
  } = $props();
36
37
 
37
- const DEFAULT_PATTERN_CONFIG = {
38
- repeat: 1,
39
- repeatDirection: 'vertical' as const,
40
- repeatGap: '24px'
41
- };
42
-
43
- // プリセットパターンの定義
44
- const PRESET_PATTERNS: Record<string, SkeletonPatternConfig[]> = {
45
- 'article-detail': [
46
- {
47
- type: 'box'
48
- },
49
- { type: 'avatar', showName: true },
50
- {
51
- type: 'text',
52
- lines: 5,
53
- repeat: 1
54
- }
55
- ],
56
- 'article-list': [
57
- {
58
- type: 'media',
59
- layout: 'horizontal',
60
- thumbnailConfig: { width: '160px', aspectRatio: '4/3' },
61
- textConfig: { lines: 3 },
62
- repeat: 3
63
- }
64
- ],
65
- 'product-list': [
66
- {
67
- type: 'media',
68
- layout: 'vertical',
69
- thumbnailConfig: { width: '100%', aspectRatio: '1' },
70
- textConfig: { lines: 2 },
71
- repeat: 4,
72
- repeatDirection: 'horizontal'
73
- }
74
- ],
75
- 'video-list': [
76
- {
77
- type: 'media',
78
- layout: 'vertical',
79
- thumbnailConfig: { width: '100%', aspectRatio: '16/9' },
80
- textConfig: { lines: 2 },
81
- repeat: 3,
82
- repeatDirection: 'horizontal'
83
- }
84
- ],
85
- 'user-list': [
86
- {
87
- type: 'avatar',
88
- showName: true
89
- }
90
- ],
91
- 'button-group': [
92
- {
93
- type: 'button',
94
- width: '120px',
95
- repeat: 2,
96
- repeatDirection: 'horizontal',
97
- repeatGap: '16px'
98
- }
99
- ]
100
- };
101
-
102
38
  // =========================================================================
103
39
  // $derived
104
40
  // =========================================================================
@@ -162,17 +98,19 @@
162
98
  });
163
99
 
164
100
  const repeatGapStyle = $derived(getStyleFromNumber(repeatGap));
165
- const itemGapStyle = $derived(getStyleFromNumber(itemGap));
101
+ const patternGapStyle = $derived(getStyleFromNumber(patternGap));
166
102
  </script>
167
103
 
168
104
  <div class={containerClasses} style={customStyle} data-testid="skeleton">
169
105
  <div class="skeleton__items" style="gap: {repeatGapStyle};">
170
106
  {#each Array(repeat) as _, index}
171
- <div class="skeleton__item" style="gap: {itemGapStyle};">
107
+ <div class="skeleton__item" style="gap: {patternGapStyle};">
172
108
  {#each mergedPatterns as patternConfig}
173
109
  {@const patternRepeat = patternConfig.repeat || 1}
174
110
  {@const patternRepeatDirection = patternConfig.repeatDirection || 'vertical'}
175
- {@const patternRepeatGap = getStyleFromNumber(patternConfig.repeatGap) || '8px'}
111
+ {@const patternRepeatGap =
112
+ getStyleFromNumber(patternConfig.repeatGap) ||
113
+ getStyleFromNumber(DEFAULT_PATTERN_CONFIG.repeatGap)}
176
114
  <div
177
115
  class="skeleton__pattern"
178
116
  class:skeleton__pattern--horizontal={patternRepeatDirection === 'horizontal'}
@@ -3,7 +3,7 @@ type $$ComponentProps = {
3
3
  patterns?: SkeletonPatternConfig[];
4
4
  repeat?: number;
5
5
  repeatGap?: string | number;
6
- itemGap?: string | number;
6
+ patternGap?: string | number;
7
7
  className?: string;
8
8
  customStyle?: string;
9
9
  animated?: boolean;
@@ -4,11 +4,12 @@
4
4
  import SkeletonBox from './SkeletonBox.svelte';
5
5
  import SkeletonText from './SkeletonText.svelte';
6
6
  import { getStyleFromNumber } from '../../utils/style';
7
- import type {
8
- SkeletonAvatarConfig,
9
- SkeletonTextConfig,
10
- SkeletonAvatarImageConfig
11
- } from '../../types/skeleton';
7
+ import type { SkeletonAvatarConfig } from '../../types/skeleton';
8
+ import {
9
+ DEFAULT_AVATAR_IMAGE_CONFIG,
10
+ DEFAULT_TEXT_CONFIG_AVATAR,
11
+ DEFAULT_AVATAR_CONFIG
12
+ } from '../../constants/skeleton';
12
13
 
13
14
  // =========================================================================
14
15
  // Props
@@ -23,25 +24,6 @@
23
24
  animated?: boolean;
24
25
  } = $props();
25
26
 
26
- // デフォルト設定
27
- const DEFAULT_AVATAR_IMAGE_CONFIG: SkeletonAvatarImageConfig = {
28
- type: 'avatar-image',
29
- size: '48px',
30
- radius: 'var(--svelte-ui-skeleton-avatar-image-border-radius)',
31
- customStyle: ''
32
- };
33
- const DEFAULT_TEXT_CONFIG: SkeletonTextConfig = {
34
- type: 'text',
35
- width: '160px',
36
- lines: 1,
37
- customStyle: ''
38
- };
39
- const DEFAULT_AVATAR_CONFIG: SkeletonAvatarConfig = {
40
- type: 'avatar',
41
- avatarImageConfig: DEFAULT_AVATAR_IMAGE_CONFIG,
42
- textConfig: DEFAULT_TEXT_CONFIG
43
- };
44
-
45
27
  // マージされた設定
46
28
  const mergedAvatarImageConfig = $derived({
47
29
  ...DEFAULT_AVATAR_IMAGE_CONFIG,
@@ -49,7 +31,7 @@
49
31
  });
50
32
 
51
33
  const mergedTextConfig = $derived({
52
- ...DEFAULT_TEXT_CONFIG,
34
+ ...DEFAULT_TEXT_CONFIG_AVATAR,
53
35
  ...(avatarConfig.textConfig || {})
54
36
  });
55
37
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  <script lang="ts">
4
4
  import { getStyleFromNumber } from '../../utils/style';
5
+ import { DEFAULT_BOX_CONFIG } from '../../constants/skeleton';
5
6
 
6
7
  // =========================================================================
7
8
  // Props
@@ -26,16 +27,6 @@
26
27
  customStyle?: string;
27
28
  } = $props();
28
29
 
29
- // =========================================================================
30
- // Constants
31
- // =========================================================================
32
-
33
- const DEFAULT_BOX_CONFIG = {
34
- width: '100%',
35
- height: '240px',
36
- radius: 'var(--svelte-ui-skeleton-box-border-radius, 8px)'
37
- };
38
-
39
30
  // =========================================================================
40
31
  // $derived
41
32
  // =========================================================================
@@ -86,7 +77,7 @@
86
77
  .skeleton-box__content {
87
78
  width: 100%;
88
79
  height: 100%;
89
- background-color: var(--svelte-ui-skeleton-bg-color, #e5e7eb);
80
+ background-color: var(--svelte-ui-skeleton-color);
90
81
  display: block;
91
82
  }
92
83
 
@@ -4,6 +4,7 @@
4
4
  import SkeletonBox from './SkeletonBox.svelte';
5
5
  import { getStyleFromNumber } from '../../utils/style';
6
6
  import type { SkeletonButtonConfig } from '../../types/skeleton';
7
+ import { DEFAULT_BUTTON_CONFIG } from '../../constants/skeleton';
7
8
 
8
9
  let {
9
10
  buttonConfig = {},
@@ -13,16 +14,6 @@
13
14
  animated?: boolean;
14
15
  } = $props();
15
16
 
16
- // デフォルト設定
17
- const DEFAULT_BUTTON_CONFIG: SkeletonButtonConfig = {
18
- type: 'button',
19
- width: '120px',
20
- height: '36px',
21
- radius: 'var(--svelte-ui-skeleton-button-border-radius)',
22
- align: 'left',
23
- customStyle: ''
24
- };
25
-
26
17
  // マージされた設定
27
18
  const mergedButtonConfig = $derived({
28
19
  ...DEFAULT_BUTTON_CONFIG,
@@ -4,6 +4,7 @@
4
4
  import SkeletonText from './SkeletonText.svelte';
5
5
  import { getStyleFromNumber } from '../../utils/style';
6
6
  import type { SkeletonHeadingConfig } from '../../types/skeleton';
7
+ import { DEFAULT_HEADING_CONFIG } from '../../constants/skeleton';
7
8
 
8
9
  // =========================================================================
9
10
  // Props
@@ -17,14 +18,6 @@
17
18
  animated?: boolean;
18
19
  } = $props();
19
20
 
20
- // デフォルト設定
21
- const DEFAULT_HEADING_CONFIG: SkeletonHeadingConfig = {
22
- type: 'heading',
23
- width: '50%',
24
- fontSize: '2rem',
25
- customStyle: ''
26
- };
27
-
28
21
  // マージされた設定
29
22
  const mergedHeadingConfig = $derived({
30
23
  ...DEFAULT_HEADING_CONFIG,
@@ -36,11 +29,18 @@
36
29
  // =========================================================================
37
30
 
38
31
  const widthStyle = $derived(getStyleFromNumber(mergedHeadingConfig.width));
39
- const fontSizeStyle = $derived(getStyleFromNumber(mergedHeadingConfig.fontSize));
32
+ const fontSizeStyle = $derived(
33
+ mergedHeadingConfig.fontSize
34
+ ? typeof mergedHeadingConfig.fontSize === 'string' &&
35
+ mergedHeadingConfig.fontSize.startsWith('var(')
36
+ ? mergedHeadingConfig.fontSize
37
+ : getStyleFromNumber(mergedHeadingConfig.fontSize)
38
+ : DEFAULT_HEADING_CONFIG.fontSize
39
+ );
40
40
  </script>
41
41
 
42
42
  <div class="skeleton-heading" style="font-size: {fontSizeStyle}; {mergedHeadingConfig.customStyle}">
43
- <SkeletonText textConfig={{ width: widthStyle, fontSize: fontSizeStyle }} {animated} />
43
+ <SkeletonText textConfig={{ width: widthStyle }} {animated} />
44
44
  </div>
45
45
 
46
46
  <style>.skeleton-heading {
@@ -4,11 +4,12 @@
4
4
  import SkeletonBox from './SkeletonBox.svelte';
5
5
  import SkeletonText from './SkeletonText.svelte';
6
6
  import { getStyleFromNumber } from '../../utils/style';
7
- import type {
8
- SkeletonMediaConfig,
9
- SkeletonThumbnailConfig,
10
- SkeletonTextConfig
11
- } from '../../types/skeleton';
7
+ import type { SkeletonMediaConfig } from '../../types/skeleton';
8
+ import {
9
+ DEFAULT_MEDIA_CONFIG,
10
+ DEFAULT_THUMBNAIL_CONFIG,
11
+ DEFAULT_TEXT_CONFIG_MEDIA
12
+ } from '../../constants/skeleton';
12
13
 
13
14
  let {
14
15
  width = '100%',
@@ -20,14 +21,6 @@
20
21
  animated?: boolean;
21
22
  } = $props();
22
23
 
23
- // デフォルト設定
24
- const DEFAULT_MEDIA_CONFIG: Partial<SkeletonMediaConfig> = {
25
- type: 'media',
26
- layout: 'horizontal',
27
- thumbnailConfig: { width: '160px', aspectRatio: '16/9' },
28
- textConfig: { width: '100%', lines: 2 }
29
- };
30
-
31
24
  // マージされた設定
32
25
  const mergedMediaConfig = $derived({
33
26
  ...DEFAULT_MEDIA_CONFIG,
@@ -37,20 +30,6 @@
37
30
  // レイアウト方向を取得
38
31
  const layoutDirection = $derived(mergedMediaConfig.layout || 'horizontal');
39
32
 
40
- // デフォルト設定
41
- const DEFAULT_THUMBNAIL_CONFIG: SkeletonThumbnailConfig = {
42
- type: 'thumbnail',
43
- width: '120px',
44
- radius: 'var(--svelte-ui-skeleton-button-border-radius)',
45
- customStyle: ''
46
- };
47
- const DEFAULT_TEXT_CONFIG: SkeletonTextConfig = {
48
- type: 'text',
49
- width: '100%',
50
- lines: 2,
51
- customStyle: ''
52
- };
53
-
54
33
  // マージされた設定
55
34
  const mergedThumbnailConfig = $derived({
56
35
  ...DEFAULT_THUMBNAIL_CONFIG,
@@ -58,7 +37,7 @@
58
37
  });
59
38
 
60
39
  const mergedTextConfig = $derived({
61
- ...DEFAULT_TEXT_CONFIG,
40
+ ...DEFAULT_TEXT_CONFIG_MEDIA,
62
41
  ...(mergedMediaConfig.textConfig || {})
63
42
  });
64
43
 
@@ -103,13 +82,13 @@
103
82
  .skeleton-media {
104
83
  display: grid;
105
84
  grid-template-columns: auto 1fr;
106
- gap: 16px;
85
+ gap: var(--svelte-ui-skeleton-media-gap);
107
86
  }
108
87
 
109
88
  .skeleton-media--vertical {
110
89
  grid-template-columns: 1fr;
111
90
  grid-template-rows: auto 1fr;
112
91
  justify-items: center;
113
- gap: 8px;
92
+ gap: var(--svelte-ui-skeleton-media-vertical-gap);
114
93
  }
115
94
  </style>
@@ -4,6 +4,7 @@
4
4
  import SkeletonBox from './SkeletonBox.svelte';
5
5
  import { getStyleFromNumber } from '../../utils/style';
6
6
  import type { SkeletonTextConfig } from '../../types/skeleton';
7
+ import { DEFAULT_TEXT_CONFIG } from '../../constants/skeleton';
7
8
 
8
9
  // =========================================================================
9
10
  // Props
@@ -18,14 +19,6 @@
18
19
  animated?: boolean;
19
20
  } = $props();
20
21
 
21
- // デフォルト設定
22
- const DEFAULT_TEXT_CONFIG: SkeletonTextConfig = {
23
- type: 'text',
24
- width: '100%',
25
- lines: 1,
26
- customStyle: ''
27
- };
28
-
29
22
  // マージされた設定
30
23
  const mergedTextConfig = $derived({
31
24
  ...DEFAULT_TEXT_CONFIG,
@@ -55,7 +48,7 @@
55
48
  <div class="skeleton-text__line" style="width: {widthStyle}">
56
49
  <SkeletonBox
57
50
  width="100%"
58
- height="1.2em"
51
+ height="var(--svelte-ui-skeleton-text-line-height)"
59
52
  radius="var(--svelte-ui-skeleton-text-border-radius)"
60
53
  {animated}
61
54
  />
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@14ch/svelte-ui",
3
3
  "description": "Modern Svelte UI components library with TypeScript support",
4
4
  "private": false,
5
- "version": "0.0.8",
5
+ "version": "0.0.10",
6
6
  "type": "module",
7
7
  "keywords": [
8
8
  "svelte",