@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.
- package/dist/assets/styles/variables.scss +15 -16
- package/dist/components/ColorPicker.svelte +16 -11
- package/dist/components/ColorPicker.svelte.d.ts +2 -2
- package/dist/components/Combobox.svelte +3 -3
- package/dist/components/Combobox.svelte.d.ts +2 -2
- package/dist/components/ConfirmDialog.svelte +8 -8
- package/dist/components/ConfirmDialog.svelte.d.ts +2 -2
- package/dist/components/DatepickerCalendar.svelte +27 -12
- package/dist/components/DatepickerCalendar.svelte.d.ts +1 -1
- package/dist/components/Input.svelte +95 -14
- package/dist/components/Input.svelte.d.ts +3 -2
- package/dist/components/Radio.svelte +1 -1
- package/dist/components/Radio.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 -2
- package/dist/components/Slider.svelte.d.ts +2 -2
- package/dist/components/Textarea.svelte +91 -20
- package/dist/components/Textarea.svelte.d.ts +3 -2
- package/dist/components/skeleton/Skeleton.svelte +9 -71
- package/dist/components/skeleton/Skeleton.svelte.d.ts +1 -1
- package/dist/components/skeleton/SkeletonAvatar.svelte +7 -25
- package/dist/components/skeleton/SkeletonBox.svelte +2 -11
- package/dist/components/skeleton/SkeletonButton.svelte +1 -10
- package/dist/components/skeleton/SkeletonHeading.svelte +10 -10
- package/dist/components/skeleton/SkeletonMedia.svelte +9 -30
- package/dist/components/skeleton/SkeletonText.svelte +2 -9
- package/package.json +1 -1
|
@@ -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-
|
|
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-
|
|
693
|
-
--svelte-ui-skeleton-
|
|
694
|
-
--svelte-ui-skeleton-
|
|
695
|
-
--svelte-ui-skeleton-
|
|
696
|
-
--svelte-ui-skeleton-
|
|
697
|
-
--svelte-ui-skeleton-
|
|
698
|
-
--svelte-ui-skeleton-
|
|
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:
|
|
96
|
-
oninput?: (value:
|
|
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
|
-
|
|
151
|
+
handleValueChange();
|
|
152
152
|
});
|
|
153
153
|
});
|
|
154
154
|
|
|
@@ -167,10 +167,11 @@
|
|
|
167
167
|
// Methods
|
|
168
168
|
// =========================================================================
|
|
169
169
|
|
|
170
|
-
const handleChange = (
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
|
184
|
+
const handleValueChange = (): void => {
|
|
185
|
+
handleChange(localValue);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const handleInput = (inputValue: string | number): void => {
|
|
184
189
|
if (disabled) return;
|
|
185
|
-
oninput?.(
|
|
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
|
-
|
|
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={
|
|
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:
|
|
17
|
-
oninput?: (value:
|
|
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:
|
|
112
|
-
oninput?: (value:
|
|
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?.(
|
|
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:
|
|
24
|
-
oninput?: (value:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
62
|
-
|
|
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={
|
|
107
|
+
onclick={handleSubmit}
|
|
108
108
|
>
|
|
109
|
-
{
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
393
|
+
const range = getNormalizedRange();
|
|
394
|
+
if (!range) return false;
|
|
383
395
|
if (isRangePreviewActive) return false;
|
|
384
|
-
return dayjs(date).isSame(
|
|
396
|
+
return dayjs(date).isSame(range.start);
|
|
385
397
|
};
|
|
386
398
|
|
|
387
399
|
const isRangeEnd = (date: dayjs.Dayjs) => {
|
|
388
|
-
|
|
400
|
+
const range = getNormalizedRange();
|
|
401
|
+
if (!range) return false;
|
|
389
402
|
if (isRangePreviewActive) return false;
|
|
390
|
-
return dayjs(date).isSame(
|
|
403
|
+
return dayjs(date).isSame(range.end);
|
|
391
404
|
};
|
|
392
405
|
|
|
393
406
|
const isRangeMiddle = (date: dayjs.Dayjs) => {
|
|
394
|
-
|
|
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
|
-
|
|
414
|
+
const range = getNormalizedRange();
|
|
415
|
+
if (!range) return false;
|
|
401
416
|
if (isRangePreviewActive) return false;
|
|
402
417
|
return isRangeStart(date) && isRangeEnd(date);
|
|
403
418
|
};
|
|
@@ -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:
|
|
187
|
-
oninput?: (value:
|
|
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:
|
|
64
|
-
oninput?: (value:
|
|
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;
|
|
@@ -11,7 +11,7 @@ type $$ComponentProps = {
|
|
|
11
11
|
disabled?: boolean;
|
|
12
12
|
required?: boolean;
|
|
13
13
|
reducedMotion?: boolean;
|
|
14
|
-
onchange?: (value:
|
|
14
|
+
onchange?: (value: string | number | boolean) => void;
|
|
15
15
|
onfocus?: Function;
|
|
16
16
|
onblur?: Function;
|
|
17
17
|
onkeydown?: Function;
|
|
@@ -39,7 +39,7 @@ type $$ComponentProps = {
|
|
|
39
39
|
onpointerleave?: Function;
|
|
40
40
|
onpointermove?: Function;
|
|
41
41
|
onpointercancel?: Function;
|
|
42
|
-
onchange?: (value:
|
|
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?:
|
|
130
|
-
oninput?:
|
|
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?:
|
|
41
|
-
oninput?:
|
|
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 =
|
|
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:
|
|
164
|
-
oninput?: (value:
|
|
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
|
-
|
|
329
|
-
|
|
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) => ' '.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行分の高さを確保するためにダミーの を入れる
|
|
353
|
+
// (placeholder がある場合は :empty::before でプレースホルダを表示したいので空にしておく)
|
|
354
|
+
if (inline && !placeholder) {
|
|
355
|
+
return ' ';
|
|
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="{
|
|
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}; {
|
|
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
|
-
|
|
478
|
-
|
|
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
|
-
|
|
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:
|
|
57
|
-
oninput?: (value:
|
|
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 = '
|
|
23
|
-
|
|
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
|
-
|
|
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
|
|
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: {
|
|
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 =
|
|
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'}
|
|
@@ -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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
...
|
|
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-
|
|
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(
|
|
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
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
...
|
|
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:
|
|
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:
|
|
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="
|
|
51
|
+
height="var(--svelte-ui-skeleton-text-line-height)"
|
|
59
52
|
radius="var(--svelte-ui-skeleton-text-border-radius)"
|
|
60
53
|
{animated}
|
|
61
54
|
/>
|