@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.
- package/README.md +359 -0
- package/dist/assets/styles/README.md +144 -0
- package/dist/assets/styles/core.scss +61 -0
- package/dist/assets/styles/import.scss +11 -0
- package/dist/assets/styles/optional/fonts.scss +23 -0
- package/dist/assets/styles/optional/reset.scss +230 -0
- package/dist/assets/styles/variables.scss +805 -0
- package/dist/components/Button.svelte +574 -0
- package/dist/components/Button.svelte.d.ts +56 -0
- package/dist/components/COMPONENT_DESIGN_GUIDELINES.md +127 -0
- package/dist/components/Checkbox.svelte +523 -0
- package/dist/components/Checkbox.svelte.d.ts +42 -0
- package/dist/components/CheckboxGroup.svelte +82 -0
- package/dist/components/CheckboxGroup.svelte.d.ts +13 -0
- package/dist/components/ColorPicker.svelte +496 -0
- package/dist/components/ColorPicker.svelte.d.ts +45 -0
- package/dist/components/Combobox.svelte +576 -0
- package/dist/components/Combobox.svelte.d.ts +52 -0
- package/dist/components/ConfirmDialog.svelte +116 -0
- package/dist/components/ConfirmDialog.svelte.d.ts +20 -0
- package/dist/components/Datepicker.svelte +578 -0
- package/dist/components/Datepicker.svelte.d.ts +72 -0
- package/dist/components/DatepickerCalendar.svelte +925 -0
- package/dist/components/DatepickerCalendar.svelte.d.ts +31 -0
- package/dist/components/Dialog.svelte +245 -0
- package/dist/components/Dialog.svelte.d.ts +38 -0
- package/dist/components/Drawer.svelte +383 -0
- package/dist/components/Drawer.svelte.d.ts +39 -0
- package/dist/components/Fab.svelte +486 -0
- package/dist/components/Fab.svelte.d.ts +51 -0
- package/dist/components/FileUploader.svelte +456 -0
- package/dist/components/FileUploader.svelte.d.ts +36 -0
- package/dist/components/Icon.svelte +167 -0
- package/dist/components/Icon.svelte.d.ts +21 -0
- package/dist/components/IconButton.svelte +557 -0
- package/dist/components/IconButton.svelte.d.ts +60 -0
- package/dist/components/ImageUploader.svelte +516 -0
- package/dist/components/ImageUploader.svelte.d.ts +37 -0
- package/dist/components/ImageUploaderPreview.svelte +157 -0
- package/dist/components/ImageUploaderPreview.svelte.d.ts +13 -0
- package/dist/components/Input.svelte +885 -0
- package/dist/components/Input.svelte.d.ts +75 -0
- package/dist/components/LoadingSpinner.svelte +116 -0
- package/dist/components/LoadingSpinner.svelte.d.ts +10 -0
- package/dist/components/Modal.svelte +313 -0
- package/dist/components/Modal.svelte.d.ts +34 -0
- package/dist/components/Pagination.svelte +276 -0
- package/dist/components/Pagination.svelte.d.ts +14 -0
- package/dist/components/Popup.svelte +676 -0
- package/dist/components/Popup.svelte.d.ts +40 -0
- package/dist/components/PopupMenu.svelte +421 -0
- package/dist/components/PopupMenu.svelte.d.ts +24 -0
- package/dist/components/PopupMenuButton.svelte +365 -0
- package/dist/components/PopupMenuButton.svelte.d.ts +42 -0
- package/dist/components/Radio.svelte +548 -0
- package/dist/components/Radio.svelte.d.ts +42 -0
- package/dist/components/RadioGroup.svelte +74 -0
- package/dist/components/RadioGroup.svelte.d.ts +14 -0
- package/dist/components/Select.svelte +479 -0
- package/dist/components/Select.svelte.d.ts +47 -0
- package/dist/components/Slider.svelte +473 -0
- package/dist/components/Slider.svelte.d.ts +46 -0
- package/dist/components/Snackbar.svelte +124 -0
- package/dist/components/Snackbar.svelte.d.ts +9 -0
- package/dist/components/SnackbarItem.svelte +423 -0
- package/dist/components/SnackbarItem.svelte.d.ts +21 -0
- package/dist/components/Switch.svelte +454 -0
- package/dist/components/Switch.svelte.d.ts +40 -0
- package/dist/components/Tab.svelte +193 -0
- package/dist/components/Tab.svelte.d.ts +14 -0
- package/dist/components/TabItem.svelte +140 -0
- package/dist/components/TabItem.svelte.d.ts +17 -0
- package/dist/components/Textarea.svelte +702 -0
- package/dist/components/Textarea.svelte.d.ts +64 -0
- package/dist/components/skeleton/Skeleton.svelte +235 -0
- package/dist/components/skeleton/Skeleton.svelte.d.ts +13 -0
- package/dist/components/skeleton/SkeletonAvatar.svelte +97 -0
- package/dist/components/skeleton/SkeletonAvatar.svelte.d.ts +8 -0
- package/dist/components/skeleton/SkeletonBox.svelte +105 -0
- package/dist/components/skeleton/SkeletonBox.svelte.d.ts +12 -0
- package/dist/components/skeleton/SkeletonButton.svelte +71 -0
- package/dist/components/skeleton/SkeletonButton.svelte.d.ts +8 -0
- package/dist/components/skeleton/SkeletonHeading.svelte +49 -0
- package/dist/components/skeleton/SkeletonHeading.svelte.d.ts +8 -0
- package/dist/components/skeleton/SkeletonMedia.svelte +115 -0
- package/dist/components/skeleton/SkeletonMedia.svelte.d.ts +9 -0
- package/dist/components/skeleton/SkeletonText.svelte +75 -0
- package/dist/components/skeleton/SkeletonText.svelte.d.ts +8 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +43 -0
- package/dist/types/icon.d.ts +4 -0
- package/dist/types/icon.js +2 -0
- package/dist/types/menuItem.d.ts +8 -0
- package/dist/types/menuItem.js +1 -0
- package/dist/types/options.d.ts +6 -0
- package/dist/types/options.js +4 -0
- package/dist/types/skeleton.d.ts +77 -0
- package/dist/types/skeleton.js +19 -0
- package/dist/utils/accessibility.d.ts +48 -0
- package/dist/utils/accessibility.js +87 -0
- package/dist/utils/formatText.d.ts +4 -0
- package/dist/utils/formatText.js +44 -0
- package/dist/utils/mobile.d.ts +9 -0
- package/dist/utils/mobile.js +47 -0
- package/dist/utils/snackbar.svelte.d.ts +51 -0
- package/dist/utils/snackbar.svelte.js +107 -0
- package/dist/utils/style.d.ts +17 -0
- package/dist/utils/style.js +22 -0
- 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;
|