@14ch/svelte-ui 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/README.md +359 -0
  2. package/dist/assets/styles/README.md +144 -0
  3. package/dist/assets/styles/core.scss +61 -0
  4. package/dist/assets/styles/import.scss +11 -0
  5. package/dist/assets/styles/optional/fonts.scss +23 -0
  6. package/dist/assets/styles/optional/reset.scss +230 -0
  7. package/dist/assets/styles/variables.scss +805 -0
  8. package/dist/components/Button.svelte +574 -0
  9. package/dist/components/Button.svelte.d.ts +56 -0
  10. package/dist/components/COMPONENT_DESIGN_GUIDELINES.md +127 -0
  11. package/dist/components/Checkbox.svelte +523 -0
  12. package/dist/components/Checkbox.svelte.d.ts +42 -0
  13. package/dist/components/CheckboxGroup.svelte +82 -0
  14. package/dist/components/CheckboxGroup.svelte.d.ts +13 -0
  15. package/dist/components/ColorPicker.svelte +496 -0
  16. package/dist/components/ColorPicker.svelte.d.ts +45 -0
  17. package/dist/components/Combobox.svelte +576 -0
  18. package/dist/components/Combobox.svelte.d.ts +52 -0
  19. package/dist/components/ConfirmDialog.svelte +116 -0
  20. package/dist/components/ConfirmDialog.svelte.d.ts +20 -0
  21. package/dist/components/Datepicker.svelte +578 -0
  22. package/dist/components/Datepicker.svelte.d.ts +72 -0
  23. package/dist/components/DatepickerCalendar.svelte +925 -0
  24. package/dist/components/DatepickerCalendar.svelte.d.ts +31 -0
  25. package/dist/components/Dialog.svelte +245 -0
  26. package/dist/components/Dialog.svelte.d.ts +38 -0
  27. package/dist/components/Drawer.svelte +383 -0
  28. package/dist/components/Drawer.svelte.d.ts +39 -0
  29. package/dist/components/Fab.svelte +486 -0
  30. package/dist/components/Fab.svelte.d.ts +51 -0
  31. package/dist/components/FileUploader.svelte +456 -0
  32. package/dist/components/FileUploader.svelte.d.ts +36 -0
  33. package/dist/components/Icon.svelte +167 -0
  34. package/dist/components/Icon.svelte.d.ts +21 -0
  35. package/dist/components/IconButton.svelte +557 -0
  36. package/dist/components/IconButton.svelte.d.ts +60 -0
  37. package/dist/components/ImageUploader.svelte +516 -0
  38. package/dist/components/ImageUploader.svelte.d.ts +37 -0
  39. package/dist/components/ImageUploaderPreview.svelte +157 -0
  40. package/dist/components/ImageUploaderPreview.svelte.d.ts +13 -0
  41. package/dist/components/Input.svelte +885 -0
  42. package/dist/components/Input.svelte.d.ts +75 -0
  43. package/dist/components/LoadingSpinner.svelte +116 -0
  44. package/dist/components/LoadingSpinner.svelte.d.ts +10 -0
  45. package/dist/components/Modal.svelte +313 -0
  46. package/dist/components/Modal.svelte.d.ts +34 -0
  47. package/dist/components/Pagination.svelte +276 -0
  48. package/dist/components/Pagination.svelte.d.ts +14 -0
  49. package/dist/components/Popup.svelte +676 -0
  50. package/dist/components/Popup.svelte.d.ts +40 -0
  51. package/dist/components/PopupMenu.svelte +421 -0
  52. package/dist/components/PopupMenu.svelte.d.ts +24 -0
  53. package/dist/components/PopupMenuButton.svelte +365 -0
  54. package/dist/components/PopupMenuButton.svelte.d.ts +42 -0
  55. package/dist/components/Radio.svelte +548 -0
  56. package/dist/components/Radio.svelte.d.ts +42 -0
  57. package/dist/components/RadioGroup.svelte +74 -0
  58. package/dist/components/RadioGroup.svelte.d.ts +14 -0
  59. package/dist/components/Select.svelte +479 -0
  60. package/dist/components/Select.svelte.d.ts +47 -0
  61. package/dist/components/Slider.svelte +473 -0
  62. package/dist/components/Slider.svelte.d.ts +46 -0
  63. package/dist/components/Snackbar.svelte +124 -0
  64. package/dist/components/Snackbar.svelte.d.ts +9 -0
  65. package/dist/components/SnackbarItem.svelte +423 -0
  66. package/dist/components/SnackbarItem.svelte.d.ts +21 -0
  67. package/dist/components/Switch.svelte +454 -0
  68. package/dist/components/Switch.svelte.d.ts +40 -0
  69. package/dist/components/Tab.svelte +193 -0
  70. package/dist/components/Tab.svelte.d.ts +14 -0
  71. package/dist/components/TabItem.svelte +140 -0
  72. package/dist/components/TabItem.svelte.d.ts +17 -0
  73. package/dist/components/Textarea.svelte +702 -0
  74. package/dist/components/Textarea.svelte.d.ts +64 -0
  75. package/dist/components/skeleton/Skeleton.svelte +235 -0
  76. package/dist/components/skeleton/Skeleton.svelte.d.ts +13 -0
  77. package/dist/components/skeleton/SkeletonAvatar.svelte +97 -0
  78. package/dist/components/skeleton/SkeletonAvatar.svelte.d.ts +8 -0
  79. package/dist/components/skeleton/SkeletonBox.svelte +105 -0
  80. package/dist/components/skeleton/SkeletonBox.svelte.d.ts +12 -0
  81. package/dist/components/skeleton/SkeletonButton.svelte +71 -0
  82. package/dist/components/skeleton/SkeletonButton.svelte.d.ts +8 -0
  83. package/dist/components/skeleton/SkeletonHeading.svelte +49 -0
  84. package/dist/components/skeleton/SkeletonHeading.svelte.d.ts +8 -0
  85. package/dist/components/skeleton/SkeletonMedia.svelte +115 -0
  86. package/dist/components/skeleton/SkeletonMedia.svelte.d.ts +9 -0
  87. package/dist/components/skeleton/SkeletonText.svelte +75 -0
  88. package/dist/components/skeleton/SkeletonText.svelte.d.ts +8 -0
  89. package/dist/index.d.ts +42 -0
  90. package/dist/index.js +43 -0
  91. package/dist/types/icon.d.ts +4 -0
  92. package/dist/types/icon.js +2 -0
  93. package/dist/types/menuItem.d.ts +8 -0
  94. package/dist/types/menuItem.js +1 -0
  95. package/dist/types/options.d.ts +6 -0
  96. package/dist/types/options.js +4 -0
  97. package/dist/types/skeleton.d.ts +77 -0
  98. package/dist/types/skeleton.js +19 -0
  99. package/dist/utils/accessibility.d.ts +48 -0
  100. package/dist/utils/accessibility.js +87 -0
  101. package/dist/utils/formatText.d.ts +4 -0
  102. package/dist/utils/formatText.js +44 -0
  103. package/dist/utils/mobile.d.ts +9 -0
  104. package/dist/utils/mobile.js +47 -0
  105. package/dist/utils/snackbar.svelte.d.ts +51 -0
  106. package/dist/utils/snackbar.svelte.js +107 -0
  107. package/dist/utils/style.d.ts +17 -0
  108. package/dist/utils/style.js +22 -0
  109. package/package.json +102 -0
@@ -0,0 +1,64 @@
1
+ import type { HTMLTextareaAttributes } from 'svelte/elements';
2
+ import type { IconVariant } from '../types/icon';
3
+ type $$ComponentProps = {
4
+ name?: string;
5
+ value: string | undefined;
6
+ placeholder?: string;
7
+ id?: string | null;
8
+ tabindex?: number | null;
9
+ maxlength?: number | null;
10
+ autocomplete?: HTMLTextareaAttributes['autocomplete'];
11
+ wrap?: 'soft' | 'hard' | null;
12
+ spellcheck?: boolean | null;
13
+ autocapitalize?: 'off' | 'none' | 'on' | 'sentences' | 'words' | 'characters' | null;
14
+ textareaAttributes?: HTMLTextareaAttributes | undefined;
15
+ rows?: number;
16
+ minHeight?: number | null;
17
+ maxHeight?: string | number | null;
18
+ inline?: boolean;
19
+ focusStyle?: 'background' | 'outline' | 'none';
20
+ fullWidth?: boolean;
21
+ fullHeight?: boolean;
22
+ width?: string | number | null;
23
+ rounded?: boolean;
24
+ customStyle?: string;
25
+ disabled?: boolean;
26
+ autoResize?: boolean;
27
+ resizable?: boolean;
28
+ clearable?: boolean;
29
+ clearButtonAriaLabel?: string;
30
+ readonly?: boolean;
31
+ required?: boolean;
32
+ iconVariant?: IconVariant;
33
+ onfocus?: Function;
34
+ onblur?: Function;
35
+ onkeydown?: Function;
36
+ onkeyup?: Function;
37
+ onclick?: Function;
38
+ onmousedown?: Function;
39
+ onmouseup?: Function;
40
+ onmouseenter?: Function;
41
+ onmouseleave?: Function;
42
+ onmouseover?: Function;
43
+ onmouseout?: Function;
44
+ oncontextmenu?: Function;
45
+ onauxclick?: Function;
46
+ ontouchstart?: Function;
47
+ ontouchend?: Function;
48
+ ontouchmove?: Function;
49
+ ontouchcancel?: Function;
50
+ onpointerdown?: Function;
51
+ onpointerup?: Function;
52
+ onpointerenter?: Function;
53
+ onpointerleave?: Function;
54
+ onpointermove?: Function;
55
+ onpointercancel?: Function;
56
+ onchange?: (value: any) => void;
57
+ oninput?: (value: any) => void;
58
+ [key: string]: any;
59
+ };
60
+ declare const Textarea: import("svelte").Component<$$ComponentProps, {
61
+ focus: () => void;
62
+ }, "value">;
63
+ type Textarea = ReturnType<typeof Textarea>;
64
+ export default Textarea;
@@ -0,0 +1,235 @@
1
+ <!-- Skeleton.svelte -->
2
+
3
+ <script lang="ts">
4
+ import SkeletonBox from './SkeletonBox.svelte';
5
+ import SkeletonText from './SkeletonText.svelte';
6
+ import SkeletonMedia from './SkeletonMedia.svelte';
7
+ import SkeletonAvatar from './SkeletonAvatar.svelte';
8
+ import SkeletonButton from './SkeletonButton.svelte';
9
+ import SkeletonHeading from './SkeletonHeading.svelte';
10
+ import { getStyleFromNumber } from '../../utils/style';
11
+ import type { SkeletonPatternConfig, SkeletonPresetConfig } from '../../types/skeleton';
12
+ import { isPresetPattern, isMediaPattern, isAvatarPattern } from '../../types/skeleton';
13
+
14
+ // =========================================================================
15
+ // Props
16
+ // =========================================================================
17
+
18
+ let {
19
+ // 基本プロパティ
20
+ patterns = [{ type: 'box' }] as SkeletonPatternConfig[],
21
+ repeat = 1,
22
+ repeatGap = '64px',
23
+ itemGap = '24px',
24
+ className = '',
25
+ customStyle = '',
26
+ animated = true
27
+ }: {
28
+ patterns?: SkeletonPatternConfig[];
29
+ repeat?: number;
30
+ repeatGap?: string | number;
31
+ itemGap?: string | number;
32
+ className?: string;
33
+ customStyle?: string;
34
+ animated?: boolean;
35
+ } = $props();
36
+
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
+ // =========================================================================
103
+ // $derived
104
+ // =========================================================================
105
+
106
+ const containerClasses = $derived(['skeleton', className].filter(Boolean).join(' '));
107
+
108
+ // パターン設定をマージ
109
+ const mergedPatterns = $derived.by(() => {
110
+ return patterns
111
+ .map((pattern) => {
112
+ // 型ガードでプリセットパターンかどうかを判定
113
+ if (isPresetPattern(pattern)) {
114
+ const presetPatternsArray = PRESET_PATTERNS[pattern.type] || [];
115
+ // プリセットパターンを展開して、元のパターンの設定で上書き
116
+ const { type: _, ...patternWithoutType } = pattern;
117
+ const typedPatternWithoutType = patternWithoutType as Omit<SkeletonPresetConfig, 'type'>;
118
+ return presetPatternsArray.map((presetPattern) => {
119
+ // プリセットパターン内の各パターンに対して、ユーザー指定のプロパティを適用
120
+ const mergedPattern = {
121
+ ...DEFAULT_PATTERN_CONFIG,
122
+ ...presetPattern,
123
+ ...typedPatternWithoutType
124
+ } as SkeletonPatternConfig;
125
+
126
+ // ネストしたオブジェクトのマージ処理(型ガードで型を絞り込む)
127
+ if (isMediaPattern(presetPattern) && 'thumbnailConfig' in typedPatternWithoutType) {
128
+ (mergedPattern as typeof presetPattern).thumbnailConfig = {
129
+ ...presetPattern.thumbnailConfig,
130
+ ...typedPatternWithoutType.thumbnailConfig
131
+ };
132
+ }
133
+
134
+ if (
135
+ (isMediaPattern(presetPattern) || isAvatarPattern(presetPattern)) &&
136
+ 'textConfig' in typedPatternWithoutType
137
+ ) {
138
+ (mergedPattern as typeof presetPattern).textConfig = {
139
+ ...presetPattern.textConfig,
140
+ ...typedPatternWithoutType.textConfig
141
+ };
142
+ }
143
+
144
+ if (isAvatarPattern(presetPattern) && 'avatarImageConfig' in typedPatternWithoutType) {
145
+ (mergedPattern as typeof presetPattern).avatarImageConfig = {
146
+ ...presetPattern.avatarImageConfig,
147
+ ...typedPatternWithoutType.avatarImageConfig
148
+ };
149
+ }
150
+
151
+ return mergedPattern;
152
+ });
153
+ }
154
+
155
+ // 通常のパターン
156
+ return {
157
+ ...DEFAULT_PATTERN_CONFIG,
158
+ ...pattern
159
+ };
160
+ })
161
+ .flat();
162
+ });
163
+
164
+ const repeatGapStyle = $derived(getStyleFromNumber(repeatGap));
165
+ const itemGapStyle = $derived(getStyleFromNumber(itemGap));
166
+ </script>
167
+
168
+ <div class={containerClasses} style={customStyle} data-testid="skeleton">
169
+ <div class="skeleton__items" style="gap: {repeatGapStyle};">
170
+ {#each Array(repeat) as _, index}
171
+ <div class="skeleton__item" style="gap: {itemGapStyle};">
172
+ {#each mergedPatterns as patternConfig}
173
+ {@const patternRepeat = patternConfig.repeat || 1}
174
+ {@const patternRepeatDirection = patternConfig.repeatDirection || 'vertical'}
175
+ {@const patternRepeatGap = getStyleFromNumber(patternConfig.repeatGap) || '8px'}
176
+ <div
177
+ class="skeleton__pattern"
178
+ class:skeleton__pattern--horizontal={patternRepeatDirection === 'horizontal'}
179
+ style="gap: {patternRepeatGap};"
180
+ >
181
+ {#each Array(patternRepeat) as _}
182
+ {#if patternConfig.type === 'box'}
183
+ {@const { type: _, width, height, radius, customStyle } = patternConfig}
184
+ <SkeletonBox {width} {height} {radius} {customStyle} {animated} />
185
+ {:else if patternConfig.type === 'heading'}
186
+ {@const { type: _, ...headingConfig } = patternConfig}
187
+ <SkeletonHeading {headingConfig} {animated} />
188
+ {:else if patternConfig.type === 'text'}
189
+ <SkeletonText textConfig={patternConfig} {animated} />
190
+ {:else if patternConfig.type === 'avatar'}
191
+ <div class="skeleton__user-list">
192
+ <SkeletonAvatar avatarConfig={patternConfig} {animated} />
193
+ </div>
194
+ {:else if patternConfig.type === 'media'}
195
+ <SkeletonMedia width={patternConfig.width} mediaConfig={patternConfig} {animated} />
196
+ {:else if patternConfig.type === 'button'}
197
+ <div class="skeleton__button">
198
+ <SkeletonButton buttonConfig={patternConfig} {animated} />
199
+ </div>
200
+ {/if}
201
+ {/each}
202
+ </div>
203
+ {/each}
204
+ </div>
205
+ {/each}
206
+ </div>
207
+ </div>
208
+
209
+ <style>.skeleton {
210
+ display: block;
211
+ width: 100%;
212
+ max-width: 100%;
213
+ overflow-x: hidden;
214
+ }
215
+
216
+ .skeleton__items {
217
+ display: flex;
218
+ flex-direction: column;
219
+ }
220
+
221
+ .skeleton__item {
222
+ display: flex;
223
+ flex-direction: column;
224
+ width: 100%;
225
+ }
226
+
227
+ .skeleton__pattern {
228
+ display: flex;
229
+ flex-direction: column;
230
+ width: 100%;
231
+ }
232
+ .skeleton__pattern--horizontal {
233
+ flex-direction: row;
234
+ align-items: center;
235
+ }</style>
@@ -0,0 +1,13 @@
1
+ import type { SkeletonPatternConfig } from '../../types/skeleton';
2
+ type $$ComponentProps = {
3
+ patterns?: SkeletonPatternConfig[];
4
+ repeat?: number;
5
+ repeatGap?: string | number;
6
+ itemGap?: string | number;
7
+ className?: string;
8
+ customStyle?: string;
9
+ animated?: boolean;
10
+ };
11
+ declare const Skeleton: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type Skeleton = ReturnType<typeof Skeleton>;
13
+ export default Skeleton;
@@ -0,0 +1,97 @@
1
+ <!-- SkeletonAvatar.svelte -->
2
+
3
+ <script lang="ts">
4
+ import SkeletonBox from './SkeletonBox.svelte';
5
+ import SkeletonText from './SkeletonText.svelte';
6
+ import { getStyleFromNumber } from '../../utils/style';
7
+ import type {
8
+ SkeletonAvatarConfig,
9
+ SkeletonTextConfig,
10
+ SkeletonAvatarImageConfig
11
+ } from '../../types/skeleton';
12
+
13
+ // =========================================================================
14
+ // Props
15
+ // =========================================================================
16
+
17
+ let {
18
+ // 基本プロパティ
19
+ avatarConfig = {},
20
+ animated = true
21
+ }: {
22
+ avatarConfig?: Partial<SkeletonAvatarConfig>;
23
+ animated?: boolean;
24
+ } = $props();
25
+
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
+ // マージされた設定
46
+ const mergedAvatarImageConfig = $derived({
47
+ ...DEFAULT_AVATAR_IMAGE_CONFIG,
48
+ ...(avatarConfig.avatarImageConfig || {})
49
+ });
50
+
51
+ const mergedTextConfig = $derived({
52
+ ...DEFAULT_TEXT_CONFIG,
53
+ ...(avatarConfig.textConfig || {})
54
+ });
55
+
56
+ const mergedAvatarConfig = $derived({
57
+ ...DEFAULT_AVATAR_CONFIG,
58
+ ...avatarConfig
59
+ });
60
+
61
+ const avatarImageSizeStyle = $derived(getStyleFromNumber(mergedAvatarImageConfig.size));
62
+ const nameWidthStyle = $derived(getStyleFromNumber(mergedTextConfig.width));
63
+ </script>
64
+
65
+ <div class="skeleton-avatar">
66
+ <div class="skeleton-avatar__avatar-image">
67
+ <SkeletonBox
68
+ width={avatarImageSizeStyle}
69
+ height={avatarImageSizeStyle}
70
+ radius="var(--svelte-ui-skeleton-avatar-image-border-radius)"
71
+ {animated}
72
+ customStyle={mergedAvatarImageConfig.customStyle}
73
+ />
74
+ </div>
75
+ {#if mergedAvatarConfig.showName}
76
+ <div class="skeleton-avatar__text">
77
+ <SkeletonText
78
+ textConfig={{
79
+ width: nameWidthStyle,
80
+ lines: mergedTextConfig.lines
81
+ }}
82
+ {animated}
83
+ />
84
+ </div>
85
+ {/if}
86
+ </div>
87
+
88
+ <style>.skeleton-avatar {
89
+ display: flex;
90
+ align-items: center;
91
+ gap: 12px;
92
+ width: 100%;
93
+ }
94
+
95
+ .skeleton-avatar > * {
96
+ flex-shrink: 0;
97
+ }</style>
@@ -0,0 +1,8 @@
1
+ import type { SkeletonAvatarConfig } from '../../types/skeleton';
2
+ type $$ComponentProps = {
3
+ avatarConfig?: Partial<SkeletonAvatarConfig>;
4
+ animated?: boolean;
5
+ };
6
+ declare const SkeletonAvatar: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type SkeletonAvatar = ReturnType<typeof SkeletonAvatar>;
8
+ export default SkeletonAvatar;
@@ -0,0 +1,105 @@
1
+ <!-- SkeletonBox.svelte -->
2
+
3
+ <script lang="ts">
4
+ import { getStyleFromNumber } from '../../utils/style';
5
+
6
+ // =========================================================================
7
+ // Props
8
+ // =========================================================================
9
+
10
+ let {
11
+ // 基本プロパティ
12
+ width,
13
+ height,
14
+ aspectRatio,
15
+ radius,
16
+ animated = true,
17
+ className = '',
18
+ customStyle = ''
19
+ }: {
20
+ width?: string | number;
21
+ height?: string | number;
22
+ aspectRatio?: string | number;
23
+ radius?: string | number;
24
+ animated?: boolean;
25
+ className?: string;
26
+ customStyle?: string;
27
+ } = $props();
28
+
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
+ // =========================================================================
40
+ // $derived
41
+ // =========================================================================
42
+
43
+ const containerClasses = $derived(
44
+ ['skeleton-box', animated && 'skeleton-box--animated', className].filter(Boolean).join(' ')
45
+ );
46
+
47
+ const mergedConfig = $derived({
48
+ ...DEFAULT_BOX_CONFIG,
49
+ width: width ?? DEFAULT_BOX_CONFIG.width,
50
+ height: height ?? DEFAULT_BOX_CONFIG.height,
51
+ radius: radius ?? DEFAULT_BOX_CONFIG.radius
52
+ });
53
+
54
+ const widthStyle = $derived(getStyleFromNumber(mergedConfig.width));
55
+ const heightStyle = $derived(getStyleFromNumber(mergedConfig.height));
56
+ const aspectRatioStyle = $derived(aspectRatio ? getStyleFromNumber(aspectRatio) : '');
57
+ const radiusStyle = $derived(getStyleFromNumber(mergedConfig.radius));
58
+
59
+ // heightとaspectRatioの優先順位を制御
60
+ const finalHeightStyle = $derived(
61
+ height ? heightStyle : aspectRatio ? '' : getStyleFromNumber(mergedConfig.height)
62
+ );
63
+ const finalAspectRatioStyle = $derived(height ? '' : aspectRatio ? aspectRatioStyle : '');
64
+ </script>
65
+
66
+ <div
67
+ class={containerClasses}
68
+ style="width: {widthStyle}; {finalHeightStyle
69
+ ? `height: ${finalHeightStyle};`
70
+ : ''} {finalAspectRatioStyle
71
+ ? `aspect-ratio: ${finalAspectRatioStyle};`
72
+ : ''} border-radius: {radiusStyle}; {customStyle}"
73
+ >
74
+ <div class="skeleton-box__content"></div>
75
+ </div>
76
+
77
+ <style>.skeleton-box {
78
+ display: block;
79
+ position: relative;
80
+ overflow: hidden;
81
+ }
82
+ .skeleton-box--animated .skeleton-box__content {
83
+ animation: skeleton-pulse 4s infinite;
84
+ }
85
+
86
+ .skeleton-box__content {
87
+ width: 100%;
88
+ height: 100%;
89
+ background-color: var(--svelte-ui-skeleton-bg-color, #e5e7eb);
90
+ display: block;
91
+ }
92
+
93
+ @keyframes skeleton-pulse {
94
+ 0% {
95
+ opacity: 1;
96
+ animation-timing-function: ease-in-out;
97
+ }
98
+ 50% {
99
+ opacity: 0.5;
100
+ animation-timing-function: ease-in-out;
101
+ }
102
+ 100% {
103
+ opacity: 1;
104
+ }
105
+ }</style>
@@ -0,0 +1,12 @@
1
+ type $$ComponentProps = {
2
+ width?: string | number;
3
+ height?: string | number;
4
+ aspectRatio?: string | number;
5
+ radius?: string | number;
6
+ animated?: boolean;
7
+ className?: string;
8
+ customStyle?: string;
9
+ };
10
+ declare const SkeletonBox: import("svelte").Component<$$ComponentProps, {}, "">;
11
+ type SkeletonBox = ReturnType<typeof SkeletonBox>;
12
+ export default SkeletonBox;
@@ -0,0 +1,71 @@
1
+ <!-- SkeletonButton.svelte -->
2
+
3
+ <script lang="ts">
4
+ import SkeletonBox from './SkeletonBox.svelte';
5
+ import { getStyleFromNumber } from '../../utils/style';
6
+ import type { SkeletonButtonConfig } from '../../types/skeleton';
7
+
8
+ let {
9
+ buttonConfig = {},
10
+ animated = true
11
+ }: {
12
+ buttonConfig?: Partial<SkeletonButtonConfig>;
13
+ animated?: boolean;
14
+ } = $props();
15
+
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
+ // マージされた設定
27
+ const mergedButtonConfig = $derived({
28
+ ...DEFAULT_BUTTON_CONFIG,
29
+ ...buttonConfig
30
+ });
31
+
32
+ const widthStyle = $derived(getStyleFromNumber(mergedButtonConfig.width));
33
+ const heightStyle = $derived(getStyleFromNumber(mergedButtonConfig.height));
34
+ const radiusStyle = $derived(
35
+ typeof mergedButtonConfig.radius === 'number'
36
+ ? `${mergedButtonConfig.radius}px`
37
+ : mergedButtonConfig.radius
38
+ );
39
+
40
+ // alignに応じたCSSクラスを生成
41
+ const alignClass = $derived(`skeleton-button--align-${mergedButtonConfig.align}`);
42
+ </script>
43
+
44
+ <div class="skeleton-button {alignClass}">
45
+ <SkeletonBox
46
+ width={widthStyle}
47
+ height={heightStyle}
48
+ radius={radiusStyle}
49
+ {animated}
50
+ customStyle={mergedButtonConfig.customStyle}
51
+ />
52
+ </div>
53
+
54
+ <style>
55
+ .skeleton-button {
56
+ display: flex;
57
+ width: 100%;
58
+ }
59
+
60
+ .skeleton-button--align-left {
61
+ justify-content: flex-start;
62
+ }
63
+
64
+ .skeleton-button--align-center {
65
+ justify-content: center;
66
+ }
67
+
68
+ .skeleton-button--align-right {
69
+ justify-content: flex-end;
70
+ }
71
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { SkeletonButtonConfig } from '../../types/skeleton';
2
+ type $$ComponentProps = {
3
+ buttonConfig?: Partial<SkeletonButtonConfig>;
4
+ animated?: boolean;
5
+ };
6
+ declare const SkeletonButton: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type SkeletonButton = ReturnType<typeof SkeletonButton>;
8
+ export default SkeletonButton;
@@ -0,0 +1,49 @@
1
+ <!-- SkeletonHeading.svelte -->
2
+
3
+ <script lang="ts">
4
+ import SkeletonText from './SkeletonText.svelte';
5
+ import { getStyleFromNumber } from '../../utils/style';
6
+ import type { SkeletonHeadingConfig } from '../../types/skeleton';
7
+
8
+ // =========================================================================
9
+ // Props
10
+ // =========================================================================
11
+
12
+ let {
13
+ headingConfig = {},
14
+ animated = true
15
+ }: {
16
+ headingConfig?: Partial<SkeletonHeadingConfig>;
17
+ animated?: boolean;
18
+ } = $props();
19
+
20
+ // デフォルト設定
21
+ const DEFAULT_HEADING_CONFIG: SkeletonHeadingConfig = {
22
+ type: 'heading',
23
+ width: '50%',
24
+ fontSize: '2rem',
25
+ customStyle: ''
26
+ };
27
+
28
+ // マージされた設定
29
+ const mergedHeadingConfig = $derived({
30
+ ...DEFAULT_HEADING_CONFIG,
31
+ ...headingConfig
32
+ });
33
+
34
+ // =========================================================================
35
+ // $derived
36
+ // =========================================================================
37
+
38
+ const widthStyle = $derived(getStyleFromNumber(mergedHeadingConfig.width));
39
+ const fontSizeStyle = $derived(getStyleFromNumber(mergedHeadingConfig.fontSize));
40
+ </script>
41
+
42
+ <div class="skeleton-heading" style="font-size: {fontSizeStyle}; {mergedHeadingConfig.customStyle}">
43
+ <SkeletonText textConfig={{ width: widthStyle, fontSize: fontSizeStyle }} {animated} />
44
+ </div>
45
+
46
+ <style>.skeleton-heading {
47
+ display: block;
48
+ width: 100%;
49
+ }</style>
@@ -0,0 +1,8 @@
1
+ import type { SkeletonHeadingConfig } from '../../types/skeleton';
2
+ type $$ComponentProps = {
3
+ headingConfig?: Partial<SkeletonHeadingConfig>;
4
+ animated?: boolean;
5
+ };
6
+ declare const SkeletonHeading: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type SkeletonHeading = ReturnType<typeof SkeletonHeading>;
8
+ export default SkeletonHeading;