@14ch/svelte-ui 0.0.3 → 0.0.4

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.
@@ -7,5 +7,7 @@
7
7
  @use './variables';
8
8
  @use './core';
9
9
 
10
- /* Material Symbols font (required for Icon component) */
10
+ /* Material Symbols fonts (required for Icon component) */
11
11
  @import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200');
12
+ @import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200');
13
+ @import url('https://fonts.googleapis.com/css2?family=Material+Symbols+Sharp:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200');
@@ -404,9 +404,9 @@
404
404
  --svelte-ui-checkbox-size: 16px;
405
405
  --svelte-ui-checkbox-size-sm: 14px;
406
406
  --svelte-ui-checkbox-size-lg: 20px;
407
- --svelte-ui-checkbox-min-height: 20px;
408
- --svelte-ui-checkbox-min-height-sm: 16px;
409
- --svelte-ui-checkbox-min-height-lg: 24px;
407
+ --svelte-ui-checkbox-min-height: 1.8em;
408
+ --svelte-ui-checkbox-min-height-sm: 1.6em;
409
+ --svelte-ui-checkbox-min-height-lg: 2.2em;
410
410
  --svelte-ui-checkbox-border-radius: 2px;
411
411
  --svelte-ui-checkbox-border-width: 2px;
412
412
  --svelte-ui-checkbox-gap: 8px;
@@ -457,11 +457,17 @@
457
457
  --svelte-ui-radio-dot-size-lg: 10px;
458
458
  --svelte-ui-radio-border-radius: var(--svelte-ui-border-radius-rounded);
459
459
  --svelte-ui-radio-border-width: 2px;
460
- --svelte-ui-radio-min-height: 20px;
461
- --svelte-ui-radio-min-height-sm: 16px;
462
- --svelte-ui-radio-min-height-lg: 24px;
460
+ --svelte-ui-radio-min-height: 1.8em;
461
+ --svelte-ui-radio-min-height-sm: 1.6em;
462
+ --svelte-ui-radio-min-height-lg: 2.2em;
463
463
  --svelte-ui-radio-gap: 8px;
464
464
  --svelte-ui-radio-line-height: 1.2em;
465
+
466
+ /* CheckboxGroup */
467
+ --svelte-ui-checkbox-group-gap: 0 24px;
468
+
469
+ /* RadioGroup */
470
+ --svelte-ui-radio-group-gap: 0 24px;
465
471
  --svelte-ui-radio-disabled-opacity: var(--svelte-ui-disabled-opacity);
466
472
  --svelte-ui-radio-border-color: var(--svelte-ui-border-color);
467
473
  --svelte-ui-radio-bg-checked: var(--svelte-ui-radio-checked-color);
@@ -41,12 +41,12 @@
41
41
  // 状態/動作
42
42
  disabled = false,
43
43
  loading = false,
44
- reducedMotion = false,
45
44
 
46
45
  // ARIA/アクセシビリティ
47
46
  ariaLabel,
48
47
  ariaDescribedby,
49
48
  ariaExpanded,
49
+ reducedMotion = false,
50
50
 
51
51
  // フォーカスイベント
52
52
  onfocus = () => {}, // No params for type inference
@@ -113,12 +113,12 @@
113
113
  // 状態/動作
114
114
  disabled?: boolean;
115
115
  loading?: boolean;
116
- reducedMotion?: boolean;
117
116
 
118
117
  // ARIA/アクセシビリティ
119
118
  ariaLabel?: string;
120
119
  ariaDescribedby?: string;
121
120
  ariaExpanded?: boolean;
121
+ reducedMotion?: boolean;
122
122
 
123
123
  // フォーカスイベント
124
124
  onfocus?: Function; // No params for type inference
@@ -22,10 +22,10 @@ type $$ComponentProps = {
22
22
  iconVariant?: IconVariant;
23
23
  disabled?: boolean;
24
24
  loading?: boolean;
25
- reducedMotion?: boolean;
26
25
  ariaLabel?: string;
27
26
  ariaDescribedby?: string;
28
27
  ariaExpanded?: boolean;
28
+ reducedMotion?: boolean;
29
29
  onfocus?: Function;
30
30
  onblur?: Function;
31
31
  onkeydown?: Function;
@@ -26,6 +26,8 @@
26
26
  // 状態/動作
27
27
  disabled = false,
28
28
  required = false,
29
+
30
+ // ARIA/アクセシビリティ
29
31
  reducedMotion = false,
30
32
 
31
33
  // 入力イベント
@@ -85,6 +87,8 @@
85
87
  // 状態/動作
86
88
  disabled?: boolean;
87
89
  required?: boolean;
90
+
91
+ // ARIA/アクセシビリティ
88
92
  reducedMotion?: boolean;
89
93
 
90
94
  // 入力イベント
@@ -11,9 +11,17 @@
11
11
 
12
12
  // スタイル/レイアウト
13
13
  direction = 'vertical',
14
- gap = '0',
14
+ gap,
15
15
  wrap = false,
16
16
  minOptionWidth,
17
+ size = 'medium',
18
+
19
+ // 状態/動作
20
+ disabled = false,
21
+ required = false,
22
+
23
+ // ARIA/アクセシビリティ
24
+ reducedMotion = false,
17
25
 
18
26
  // 入力イベント
19
27
  onchange = () => {} // No params for type inference
@@ -27,6 +35,14 @@
27
35
  gap?: string | number;
28
36
  wrap?: boolean;
29
37
  minOptionWidth?: string | number;
38
+ size?: 'small' | 'medium' | 'large';
39
+
40
+ // 状態/動作
41
+ disabled?: boolean;
42
+ required?: boolean;
43
+
44
+ // ARIA/アクセシビリティ
45
+ reducedMotion?: boolean;
30
46
 
31
47
  // 入力イベント
32
48
  onchange?: (value: OptionValue[]) => void;
@@ -44,23 +60,30 @@
44
60
  onchange(value);
45
61
  };
46
62
 
47
- const gapStyle = getStyleFromNumber(gap);
63
+ const gapStyle = gap !== undefined ? getStyleFromNumber(gap) : undefined;
48
64
 
49
65
  const minOptionWidthStyle = getStyleFromNumber(minOptionWidth);
50
66
  </script>
51
67
 
52
68
  <ul
53
69
  class="checkbox-group"
54
- style="--flex-direction: {direction === 'vertical' ? 'column' : 'row'};
55
- --gap: {gapStyle};
56
- --wrap: {wrap ? 'wrap' : 'none'};
57
- --min-option-width: {minOptionWidthStyle}
70
+ style="--svelte-ui-checkbox-group-flex-direction: {direction === 'vertical' ? 'column' : 'row'};
71
+ {gapStyle ? `--svelte-ui-checkbox-group-gap: ${gapStyle};` : ''}
72
+ --svelte-ui-checkbox-group-wrap: {wrap ? 'wrap' : 'none'};
73
+ --svelte-ui-checkbox-group-min-option-width: {minOptionWidthStyle}
58
74
  "
59
75
  >
60
76
  {#each options as option (option.value)}
61
77
  {#if localValues[option.value] !== undefined}
62
78
  <li class="checkbox-group__option">
63
- <Checkbox bind:value={localValues[option.value]} onchange={handleChange}>
79
+ <Checkbox
80
+ bind:value={localValues[option.value]}
81
+ {size}
82
+ {disabled}
83
+ {required}
84
+ {reducedMotion}
85
+ onchange={handleChange}
86
+ >
64
87
  {option.label}
65
88
  </Checkbox>
66
89
  </li>
@@ -71,12 +94,12 @@
71
94
  <style>
72
95
  .checkbox-group {
73
96
  display: flex;
74
- flex-direction: var(--flex-direction);
75
- gap: var(--gap);
76
- flex-wrap: var(--wrap);
97
+ flex-direction: var(--svelte-ui-checkbox-group-flex-direction);
98
+ gap: var(--svelte-ui-checkbox-group-gap);
99
+ flex-wrap: var(--svelte-ui-checkbox-group-wrap);
77
100
  }
78
101
 
79
102
  .checkbox-group__option {
80
- min-width: var(--min-option-width);
103
+ min-width: var(--svelte-ui-checkbox-group-min-option-width);
81
104
  }
82
105
  </style>
@@ -6,6 +6,10 @@ type $$ComponentProps = {
6
6
  gap?: string | number;
7
7
  wrap?: boolean;
8
8
  minOptionWidth?: string | number;
9
+ size?: 'small' | 'medium' | 'large';
10
+ disabled?: boolean;
11
+ required?: boolean;
12
+ reducedMotion?: boolean;
9
13
  onchange?: (value: OptionValue[]) => void;
10
14
  };
11
15
  declare const CheckboxGroup: import("svelte").Component<$$ComponentProps, {}, "value">;
@@ -230,6 +230,11 @@
230
230
  value = dataTransfer.files;
231
231
  onchange(value);
232
232
  }
233
+
234
+ // input要素をリセット
235
+ if (fileInputRef) {
236
+ fileInputRef.value = '';
237
+ }
233
238
  };
234
239
 
235
240
  export const reset = () => {
@@ -252,8 +257,8 @@
252
257
  class:file-uploader--hover={isHover}
253
258
  class:rounded
254
259
  style="
255
- --file-uploader-width: {widthStyle};
256
- --file-uploader-height: {height}px
260
+ --svelte-ui-file-uploader-width: {widthStyle};
261
+ --svelte-ui-file-uploader-height: {height}px
257
262
  "
258
263
  data-testid="file-uploader"
259
264
  onclick={handleClick}
@@ -365,8 +370,8 @@
365
370
  align-items: center;
366
371
  gap: 16px;
367
372
  position: relative;
368
- width: var(--file-uploader-width, 100%);
369
- height: var(--file-uploader-height);
373
+ width: var(--svelte-ui-file-uploader-width, 100%);
374
+ height: var(--svelte-ui-file-uploader-height);
370
375
  min-height: 100px;
371
376
  padding: 16px;
372
377
  background-color: var(--svelte-ui-file-uploader-bg);
@@ -111,11 +111,9 @@
111
111
 
112
112
  <style>
113
113
  .material-symbols-outlined,
114
- .material-symbols-filled,
115
114
  .material-symbols-rounded,
116
115
  .material-symbols-sharp {
117
116
  display: block;
118
- font-family: 'Material Symbols Outlined';
119
117
  font-size: inherit;
120
118
  color: inherit;
121
119
  line-height: inherit;
@@ -129,6 +127,18 @@
129
127
  transition-timing-function: ease;
130
128
  }
131
129
 
130
+ .material-symbols-outlined {
131
+ font-family: 'Material Symbols Outlined';
132
+ }
133
+
134
+ .material-symbols-rounded {
135
+ font-family: 'Material Symbols Rounded';
136
+ }
137
+
138
+ .material-symbols-sharp {
139
+ font-family: 'Material Symbols Sharp';
140
+ }
141
+
132
142
  /* font-variation-settings are controlled via inline styles for dynamic props */
133
143
 
134
144
  .icon-fallback-text {
@@ -47,12 +47,12 @@
47
47
  disabled = false,
48
48
  loading = false,
49
49
  pressed = false,
50
- reducedMotion = false,
51
50
 
52
51
  // ARIA/アクセシビリティ
53
52
  ariaLabel,
54
53
  ariaDescribedby,
55
54
  ariaPressed,
55
+ reducedMotion = false,
56
56
 
57
57
  // フォーカスイベント
58
58
  onfocus = () => {}, // No params for type inference
@@ -125,12 +125,12 @@
125
125
  disabled?: boolean;
126
126
  loading?: boolean;
127
127
  pressed?: boolean;
128
- reducedMotion?: boolean;
129
128
 
130
129
  // ARIA/アクセシビリティ
131
130
  ariaLabel: string;
132
131
  ariaDescribedby?: string;
133
132
  ariaPressed?: boolean;
133
+ reducedMotion?: boolean;
134
134
 
135
135
  // フォーカスイベント
136
136
  onfocus?: Function; // No params for type inference
@@ -26,10 +26,10 @@ type $$ComponentProps = {
26
26
  disabled?: boolean;
27
27
  loading?: boolean;
28
28
  pressed?: boolean;
29
- reducedMotion?: boolean;
30
29
  ariaLabel: string;
31
30
  ariaDescribedby?: string;
32
31
  ariaPressed?: boolean;
32
+ reducedMotion?: boolean;
33
33
  onfocus?: Function;
34
34
  onblur?: Function;
35
35
  onkeydown?: Function;
@@ -27,6 +27,7 @@
27
27
  width = '120px',
28
28
  height = '120px',
29
29
  rounded = false,
30
+ previewStyle = 'framed',
30
31
 
31
32
  // アイコン系
32
33
  icon = 'add_photo_alternate',
@@ -39,7 +40,7 @@
39
40
  removeFileAriaLabel = t('imageUploader.removeFile'),
40
41
 
41
42
  // 状態/動作
42
- adaptiveSize = false,
43
+ previewAdaptive = false,
43
44
 
44
45
  // 入力イベント
45
46
  onchange = () => {}, // No params for type inference
@@ -75,11 +76,11 @@
75
76
  accept?: string;
76
77
 
77
78
  // スタイル/レイアウト
78
-
79
79
  width?: string | number;
80
80
  height?: string | number;
81
81
  rounded?: boolean;
82
- adaptiveSize?: boolean;
82
+ previewStyle?: 'plain' | 'framed';
83
+ previewAdaptive?: boolean;
83
84
 
84
85
  // アイコン系
85
86
  icon?: string;
@@ -250,6 +251,11 @@
250
251
  value = dataTransfer.files;
251
252
  onchange(value);
252
253
  }
254
+
255
+ // input要素をリセット
256
+ if (fileInputRef) {
257
+ fileInputRef.value = '';
258
+ }
253
259
  };
254
260
 
255
261
  const removeFile = (index: number) => {
@@ -307,8 +313,9 @@
307
313
  {file}
308
314
  {width}
309
315
  {height}
310
- {adaptiveSize}
316
+ {previewAdaptive}
311
317
  {rounded}
318
+ {previewStyle}
312
319
  id={id ? `${id}-preview-${index}` : undefined}
313
320
  {removeFileAriaLabel}
314
321
  onRemove={() => removeFile(index)}
@@ -319,17 +326,19 @@
319
326
  class="image-uploader"
320
327
  class:image-uploader--multiple={multiple}
321
328
  class:image-uploader--rounded={rounded}
322
- class:image-uploader--adaptive={adaptiveSize}
329
+ class:image-uploader--adaptive={previewAdaptive}
323
330
  style="
324
- --image-uploader-button-width: {previewWidthStyle};
325
- --image-uploader-button-height: {previewHeightStyle};
331
+ --svelte-ui-image-uploader-button-width: {previewWidthStyle};
332
+ --svelte-ui-image-uploader-button-height: {previewHeightStyle};
326
333
  "
327
334
  data-testid="image-uploader"
328
335
  >
329
336
  {#if multiple}
330
- {#each value as file, index}
331
- {@render preview(file, index)}
332
- {/each}
337
+ {#if value}
338
+ {#each value as file, index}
339
+ {@render preview(file, index)}
340
+ {/each}
341
+ {/if}
333
342
  {/if}
334
343
  <button
335
344
  bind:this={dropAreaRef}
@@ -427,8 +436,8 @@
427
436
  align-items: center;
428
437
  position: relative;
429
438
  max-width: 100%;
430
- min-width: var(--image-uploader-button-width);
431
- min-height: var(--image-uploader-button-height);
439
+ min-width: var(--svelte-ui-image-uploader-button-width);
440
+ min-height: var(--svelte-ui-image-uploader-button-height);
432
441
  padding: 16px;
433
442
  background-color: var(--svelte-ui-form-bg);
434
443
  border-radius: var(--svelte-ui-border-radius);
@@ -9,7 +9,8 @@ type $$ComponentProps = {
9
9
  width?: string | number;
10
10
  height?: string | number;
11
11
  rounded?: boolean;
12
- adaptiveSize?: boolean;
12
+ previewStyle?: 'plain' | 'framed';
13
+ previewAdaptive?: boolean;
13
14
  icon?: string;
14
15
  iconSize?: number;
15
16
  iconFilled?: boolean;
@@ -3,27 +3,45 @@
3
3
  import IconButton from './IconButton.svelte';
4
4
  import { getStyleFromNumber } from '../utils/style';
5
5
 
6
- type ImagePreviewProps = {
7
- file: File;
8
- width?: string | number;
9
- height?: string | number;
10
- adaptiveSize?: boolean;
11
- rounded?: boolean;
12
- removeFileAriaLabel?: string;
13
- onRemove: () => void;
14
- id?: string;
15
- };
16
-
17
6
  let {
7
+ // 基本プロパティ
18
8
  file,
9
+
10
+ // HTML属性系
11
+ id,
12
+
13
+ // スタイル/レイアウト
19
14
  width = '120px',
20
15
  height = '120px',
21
- adaptiveSize = false,
16
+ previewAdaptive = false,
22
17
  rounded = false,
18
+ previewStyle = 'framed',
19
+
20
+ // ARIA/アクセシビリティ
23
21
  removeFileAriaLabel,
24
- onRemove,
25
- id
26
- }: ImagePreviewProps = $props();
22
+
23
+ // 入力イベント
24
+ onRemove
25
+ }: {
26
+ // 基本プロパティ
27
+ file: File;
28
+
29
+ // HTML属性系
30
+ id?: string;
31
+
32
+ // スタイル/レイアウト
33
+ width?: string | number;
34
+ height?: string | number;
35
+ previewAdaptive?: boolean;
36
+ rounded?: boolean;
37
+ previewStyle?: 'plain' | 'framed';
38
+
39
+ // ARIA/アクセシビリティ
40
+ removeFileAriaLabel?: string;
41
+
42
+ // 入力イベント
43
+ onRemove: () => void;
44
+ } = $props();
27
45
 
28
46
  let imageSizes = $state<Record<string, { width: number; height: number }>>({});
29
47
 
@@ -49,9 +67,9 @@
49
67
  img.src = getImageUrl(file);
50
68
  };
51
69
 
52
- // adaptiveSizeによる分岐を最初に行う
70
+ // previewAdaptiveによる分岐を最初に行う
53
71
  const imageSizeStyle = $derived.by(() => {
54
- if (adaptiveSize) {
72
+ if (previewAdaptive) {
55
73
  return `
56
74
  width: 100%;
57
75
  height: auto;
@@ -73,9 +91,11 @@
73
91
  </script>
74
92
 
75
93
  <div
94
+ {id}
76
95
  class="image-uploader-preview"
77
96
  class:image-uploader-preview--rounded={rounded}
78
- class:image-uploader-preview--adaptive={adaptiveSize}
97
+ class:image-uploader-preview--adaptive={previewAdaptive}
98
+ class:image-uploader-preview--plain={previewStyle === 'plain'}
79
99
  style={imageSizeStyle}
80
100
  >
81
101
  <img
@@ -120,6 +140,7 @@
120
140
 
121
141
  .image-uploader-preview--adaptive .image-uploader-preview__image {
122
142
  object-fit: contain;
143
+ height: auto;
123
144
  }
124
145
 
125
146
  .image-uploader-preview::after {
@@ -138,6 +159,14 @@
138
159
  border-radius: var(--svelte-ui-image-uploader-preview-border-radius-rounded);
139
160
  }
140
161
 
162
+ .image-uploader-preview--plain::after {
163
+ display: none;
164
+ }
165
+
166
+ .image-uploader-preview--plain .image-uploader-preview__image {
167
+ border-radius: 0;
168
+ }
169
+
141
170
  .image-uploader-preview__delete {
142
171
  position: absolute;
143
172
  top: 4px;
@@ -1,13 +1,14 @@
1
- type ImagePreviewProps = {
1
+ type $$ComponentProps = {
2
2
  file: File;
3
+ id?: string;
3
4
  width?: string | number;
4
5
  height?: string | number;
5
- adaptiveSize?: boolean;
6
+ previewAdaptive?: boolean;
6
7
  rounded?: boolean;
8
+ previewStyle?: 'plain' | 'framed';
7
9
  removeFileAriaLabel?: string;
8
10
  onRemove: () => void;
9
- id?: string;
10
11
  };
11
- declare const ImageUploaderPreview: import("svelte").Component<ImagePreviewProps, {}, "">;
12
+ declare const ImageUploaderPreview: import("svelte").Component<$$ComponentProps, {}, "">;
12
13
  type ImageUploaderPreview = ReturnType<typeof ImageUploaderPreview>;
13
14
  export default ImageUploaderPreview;
@@ -13,6 +13,8 @@
13
13
 
14
14
  // 状態/動作
15
15
  speed = 1,
16
+
17
+ // ARIA/アクセシビリティ
16
18
  reducedMotion = false
17
19
  }: {
18
20
  // スタイル/レイアウト
@@ -22,6 +24,8 @@
22
24
 
23
25
  // 状態/動作
24
26
  speed?: number;
27
+
28
+ // ARIA/アクセシビリティ
25
29
  reducedMotion?: boolean;
26
30
  } = $props();
27
31
 
@@ -39,13 +43,13 @@
39
43
  <div
40
44
  class="loading-spinner"
41
45
  class:spinner--no-motion={reducedMotion}
42
- style:--grow-duration="{growDuration}s"
43
- style:--rotate-duration="{rotateDuration}s"
44
- style:--spinner-size="{size}px"
45
- style:--spinner-color={color}
46
- style:--circumference={circumference}
47
- style:--half-circumference={halfCircumference}
48
- style:--negative-half-circumference={negativeHalfCircumference}
46
+ style:--svelte-ui-loading-spinner-grow-duration="{growDuration}s"
47
+ style:--svelte-ui-loading-spinner-rotate-duration="{rotateDuration}s"
48
+ style:--svelte-ui-loading-spinner-size="{size}px"
49
+ style:--svelte-ui-loading-spinner-color={color}
50
+ style:--svelte-ui-loading-spinner-circumference={circumference}
51
+ style:--svelte-ui-loading-spinner-half-circumference={halfCircumference}
52
+ style:--svelte-ui-loading-spinner-negative-half-circumference={negativeHalfCircumference}
49
53
  data-testid="loading-spinner"
50
54
  >
51
55
  <svg viewBox="0 0 {size} {size}" width={size} height={size}>
@@ -55,8 +59,8 @@
55
59
 
56
60
  <style>
57
61
  .loading-spinner {
58
- width: var(--spinner-size, var(--svelte-ui-loadingspinner-size));
59
- height: var(--spinner-size, var(--svelte-ui-loadingspinner-size));
62
+ width: var(--svelte-ui-loading-spinner-size, var(--svelte-ui-loadingspinner-size));
63
+ height: var(--svelte-ui-loading-spinner-size, var(--svelte-ui-loadingspinner-size));
60
64
  line-height: 1px;
61
65
  }
62
66
 
@@ -64,14 +68,14 @@
64
68
  width: 100%;
65
69
  height: 100%;
66
70
  transform: rotate(-90deg);
67
- animation: rotate var(--rotate-duration, 0.8s) linear infinite;
71
+ animation: rotate var(--svelte-ui-loading-spinner-rotate-duration, 0.8s) linear infinite;
68
72
  }
69
73
 
70
74
  .loading-spinner circle {
71
75
  fill: none;
72
- stroke: var(--spinner-color, var(--svelte-ui-loadingspinner-color));
76
+ stroke: var(--svelte-ui-loading-spinner-color, var(--svelte-ui-loadingspinner-color));
73
77
  stroke-linecap: butt;
74
- animation: complex-grow var(--grow-duration, 1.6s) linear infinite;
78
+ animation: complex-grow var(--svelte-ui-loading-spinner-grow-duration, 1.6s) linear infinite;
75
79
  transform-origin: center;
76
80
  }
77
81
 
@@ -87,16 +91,17 @@
87
91
  @keyframes complex-grow {
88
92
  /* 負の値も別のCSS変数として定義 */
89
93
  0% {
90
- stroke-dasharray: 0 var(--circumference);
91
- stroke-dashoffset: var(--negative-half-circumference);
94
+ stroke-dasharray: 0 var(--svelte-ui-loading-spinner-circumference);
95
+ stroke-dashoffset: var(--svelte-ui-loading-spinner-negative-half-circumference);
92
96
  }
93
97
  50% {
94
- stroke-dasharray: var(--half-circumference) var(--circumference);
98
+ stroke-dasharray: var(--svelte-ui-loading-spinner-half-circumference)
99
+ var(--svelte-ui-loading-spinner-circumference);
95
100
  stroke-dashoffset: 0;
96
101
  }
97
102
  100% {
98
- stroke-dasharray: 0 var(--circumference);
99
- stroke-dashoffset: var(--negative-half-circumference);
103
+ stroke-dasharray: 0 var(--svelte-ui-loading-spinner-circumference);
104
+ stroke-dashoffset: var(--svelte-ui-loading-spinner-negative-half-circumference);
100
105
  }
101
106
  }
102
107
 
@@ -26,6 +26,8 @@
26
26
  // 状態/動作
27
27
  disabled = false,
28
28
  required = false,
29
+
30
+ // ARIA/アクセシビリティ
29
31
  reducedMotion = false,
30
32
 
31
33
  // 入力イベント
@@ -85,6 +87,8 @@
85
87
  // 状態/動作
86
88
  disabled?: boolean;
87
89
  required?: boolean;
90
+
91
+ // ARIA/アクセシビリティ
88
92
  reducedMotion?: boolean;
89
93
 
90
94
  // 入力イベント
@@ -11,9 +11,17 @@
11
11
 
12
12
  // スタイル/レイアウト
13
13
  direction = 'vertical',
14
- gap = '0',
14
+ gap,
15
15
  wrap = false,
16
16
  minOptionWidth,
17
+ size = 'medium',
18
+
19
+ // 状態/動作
20
+ disabled = false,
21
+ required = false,
22
+
23
+ // ARIA/アクセシビリティ
24
+ reducedMotion = false,
17
25
 
18
26
  // 入力イベント
19
27
  onchange = () => {} // No params for type inference
@@ -28,32 +36,48 @@
28
36
  gap?: string | number;
29
37
  wrap?: boolean;
30
38
  minOptionWidth?: string | number;
39
+ size?: 'small' | 'medium' | 'large';
40
+
41
+ // 状態/動作
42
+ disabled?: boolean;
43
+ required?: boolean;
44
+
45
+ // ARIA/アクセシビリティ
46
+ reducedMotion?: boolean;
31
47
 
32
48
  // 入力イベント
33
49
  onchange?: (value: OptionValue) => void;
34
50
  } = $props();
35
- let localValues: Record<string, boolean> = $state({});
36
51
 
37
52
  const handleChange = () => {
38
53
  onchange(value);
39
54
  };
40
55
 
41
- const gapStyle = getStyleFromNumber(gap);
56
+ const gapStyle = gap !== undefined ? getStyleFromNumber(gap) : undefined;
42
57
 
43
58
  const minOptionWidthStyle = getStyleFromNumber(minOptionWidth);
44
59
  </script>
45
60
 
46
61
  <ul
47
- class="checkbox-group"
48
- style="--flex-direction: {direction === 'vertical' ? 'column' : 'row'};
49
- --gap: {gapStyle};
50
- --wrap: {wrap ? 'wrap' : 'none'};
51
- --min-option-width: {minOptionWidthStyle}
62
+ class="radio-group"
63
+ style="--svelte-ui-radio-group-flex-direction: {direction === 'vertical' ? 'column' : 'row'};
64
+ {gapStyle ? `--svelte-ui-radio-group-gap: ${gapStyle};` : ''}
65
+ --svelte-ui-radio-group-wrap: {wrap ? 'wrap' : 'none'};
66
+ --svelte-ui-radio-group-min-option-width: {minOptionWidthStyle}
52
67
  "
53
68
  >
54
69
  {#each options as option (option.value)}
55
- <li class="checkbox-group__option">
56
- <Radio {name} bind:currentValue={value} value={option.value} onchange={handleChange}>
70
+ <li class="radio-group__option">
71
+ <Radio
72
+ {name}
73
+ bind:currentValue={value}
74
+ value={option.value}
75
+ {size}
76
+ {disabled}
77
+ {required}
78
+ {reducedMotion}
79
+ onchange={handleChange}
80
+ >
57
81
  {option.label}
58
82
  </Radio>
59
83
  </li>
@@ -61,14 +85,14 @@
61
85
  </ul>
62
86
 
63
87
  <style>
64
- .checkbox-group {
88
+ .radio-group {
65
89
  display: flex;
66
- flex-direction: var(--flex-direction);
67
- gap: var(--gap);
68
- flex-wrap: var(--wrap);
90
+ flex-direction: var(--svelte-ui-radio-group-flex-direction);
91
+ gap: var(--svelte-ui-radio-group-gap);
92
+ flex-wrap: var(--svelte-ui-radio-group-wrap);
69
93
  }
70
94
 
71
- .checkbox-group__option {
72
- min-width: var(--min-option-width);
95
+ .radio-group__option {
96
+ min-width: var(--svelte-ui-radio-group-min-option-width);
73
97
  }
74
98
  </style>
@@ -7,6 +7,10 @@ type $$ComponentProps = {
7
7
  gap?: string | number;
8
8
  wrap?: boolean;
9
9
  minOptionWidth?: string | number;
10
+ size?: 'small' | 'medium' | 'large';
11
+ disabled?: boolean;
12
+ required?: boolean;
13
+ reducedMotion?: boolean;
10
14
  onchange?: (value: OptionValue) => void;
11
15
  };
12
16
  declare const RadioGroup: import("svelte").Component<$$ComponentProps, {}, "value">;
@@ -112,7 +112,7 @@
112
112
  );
113
113
  const requiredMargin = -containerHeight;
114
114
 
115
- snackbarRef.style.setProperty('--collapse-margin', `${requiredMargin}px`);
115
+ snackbarRef.style.setProperty('--svelte-ui-snackbar-item-collapse-margin', `${requiredMargin}px`);
116
116
 
117
117
  // CSSカスタムプロパティが確実に設定されるまで少し待つ
118
118
  requestAnimationFrame(() => {
@@ -149,7 +149,7 @@
149
149
  class="snackbar-item__content snackbar-item__content--{type} snackbar-item__content--{variant} snackbar-item__content--{position} {visible
150
150
  ? 'snackbar-item__content--visible'
151
151
  : ''}"
152
- style="--custom-color: {color ?? 'unset'}; --custom-text-color: {textColor ?? 'unset'};"
152
+ style="--svelte-ui-snackbar-item-custom-color: {color ?? 'unset'}; --svelte-ui-snackbar-item-custom-text-color: {textColor ?? 'unset'};"
153
153
  role={type === 'error' || type === 'warning' ? 'alert' : 'status'}
154
154
  aria-live={type === 'error' || type === 'warning' ? 'assertive' : 'polite'}
155
155
  aria-atomic="true"
@@ -219,7 +219,7 @@
219
219
  }
220
220
  100% {
221
221
  opacity: 0;
222
- margin-bottom: var(--collapse-margin);
222
+ margin-bottom: var(--svelte-ui-snackbar-item-collapse-margin);
223
223
  }
224
224
  }
225
225
 
@@ -230,7 +230,7 @@
230
230
  }
231
231
  100% {
232
232
  opacity: 0;
233
- margin-top: var(--collapse-margin);
233
+ margin-top: var(--svelte-ui-snackbar-item-collapse-margin);
234
234
  }
235
235
  }
236
236
 
@@ -321,103 +321,103 @@
321
321
 
322
322
  /* Type variants - filled */
323
323
  .snackbar-item__content--filled.snackbar-item__content--info {
324
- background-color: var(--custom-color, var(--svelte-ui-snackbar-info-filled-bg));
325
- color: var(--custom-text-color, var(--svelte-ui-snackbar-info-filled-text-color));
324
+ background-color: var(--svelte-ui-snackbar-item-custom-color, var(--svelte-ui-snackbar-info-filled-bg));
325
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-info-filled-text-color));
326
326
  }
327
327
 
328
328
  .snackbar-item__content--filled.snackbar-item__content--info .snackbar-item__icon {
329
- color: var(--custom-text-color, var(--svelte-ui-snackbar-info-filled-text-color));
329
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-info-filled-text-color));
330
330
  }
331
331
 
332
332
  .snackbar-item__content--filled.snackbar-item__content--success {
333
- background-color: var(--custom-color, var(--svelte-ui-snackbar-success-filled-bg));
334
- color: var(--custom-text-color, var(--svelte-ui-snackbar-success-filled-text-color));
333
+ background-color: var(--svelte-ui-snackbar-item-custom-color, var(--svelte-ui-snackbar-success-filled-bg));
334
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-success-filled-text-color));
335
335
  }
336
336
 
337
337
  .snackbar-item__content--filled.snackbar-item__content--success .snackbar-item__icon {
338
- color: var(--custom-text-color, var(--svelte-ui-snackbar-success-filled-text-color));
338
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-success-filled-text-color));
339
339
  }
340
340
 
341
341
  .snackbar-item__content--filled.snackbar-item__content--warning {
342
- background-color: var(--custom-color, var(--svelte-ui-snackbar-warning-filled-bg));
343
- color: var(--custom-text-color, var(--svelte-ui-snackbar-warning-filled-text-color));
342
+ background-color: var(--svelte-ui-snackbar-item-custom-color, var(--svelte-ui-snackbar-warning-filled-bg));
343
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-warning-filled-text-color));
344
344
  }
345
345
 
346
346
  .snackbar-item__content--filled.snackbar-item__content--warning .snackbar-item__icon {
347
- color: var(--custom-text-color, var(--svelte-ui-snackbar-warning-filled-text-color));
347
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-warning-filled-text-color));
348
348
  }
349
349
 
350
350
  .snackbar-item__content--filled.snackbar-item__content--error {
351
- background-color: var(--custom-color, var(--svelte-ui-snackbar-error-filled-bg));
352
- color: var(--custom-text-color, var(--svelte-ui-snackbar-error-filled-text-color));
351
+ background-color: var(--svelte-ui-snackbar-item-custom-color, var(--svelte-ui-snackbar-error-filled-bg));
352
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-error-filled-text-color));
353
353
  }
354
354
 
355
355
  .snackbar-item__content--filled.snackbar-item__content--error .snackbar-item__icon {
356
- color: var(--custom-text-color, var(--svelte-ui-snackbar-error-filled-text-color));
356
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-error-filled-text-color));
357
357
  }
358
358
 
359
359
  .snackbar-item__content--filled.snackbar-item__content--default {
360
- background-color: var(--custom-color, var(--svelte-ui-snackbar-default-filled-bg));
361
- color: var(--custom-text-color, var(--svelte-ui-snackbar-default-filled-text-color));
360
+ background-color: var(--svelte-ui-snackbar-item-custom-color, var(--svelte-ui-snackbar-default-filled-bg));
361
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-default-filled-text-color));
362
362
  }
363
363
 
364
364
  .snackbar-item__content--filled.snackbar-item__content--default .snackbar-item__icon {
365
- color: var(--custom-text-color, var(--svelte-ui-snackbar-default-filled-text-color));
365
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-default-filled-text-color));
366
366
  }
367
367
 
368
368
  /* Type variants - outlined */
369
369
  .snackbar-item__content--outlined.snackbar-item__content--info {
370
- background-color: var(--custom-color, var(--svelte-ui-snackbar-info-outlined-bg));
371
- color: var(--custom-text-color, var(--svelte-ui-snackbar-info-outlined-text-color));
370
+ background-color: var(--svelte-ui-snackbar-item-custom-color, var(--svelte-ui-snackbar-info-outlined-bg));
371
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-info-outlined-text-color));
372
372
  box-shadow: inset 0 0 0 1px
373
- var(--custom-color, var(--svelte-ui-snackbar-info-outlined-border-color));
373
+ var(--svelte-ui-snackbar-item-custom-color, var(--svelte-ui-snackbar-info-outlined-border-color));
374
374
  }
375
375
 
376
376
  .snackbar-item__content--outlined.snackbar-item__content--info .snackbar-item__icon {
377
- color: var(--custom-text-color, var(--svelte-ui-snackbar-info-outlined-text-color));
377
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-info-outlined-text-color));
378
378
  }
379
379
 
380
380
  .snackbar-item__content--outlined.snackbar-item__content--success {
381
- background-color: var(--custom-color, var(--svelte-ui-snackbar-success-outlined-bg));
382
- color: var(--custom-text-color, var(--svelte-ui-snackbar-success-outlined-text-color));
381
+ background-color: var(--svelte-ui-snackbar-item-custom-color, var(--svelte-ui-snackbar-success-outlined-bg));
382
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-success-outlined-text-color));
383
383
  box-shadow: inset 0 0 0 1px
384
- var(--custom-color, var(--svelte-ui-snackbar-success-outlined-border-color));
384
+ var(--svelte-ui-snackbar-item-custom-color, var(--svelte-ui-snackbar-success-outlined-border-color));
385
385
  }
386
386
 
387
387
  .snackbar-item__content--outlined.snackbar-item__content--success .snackbar-item__icon {
388
- color: var(--custom-text-color, var(--svelte-ui-snackbar-success-outlined-text-color));
388
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-success-outlined-text-color));
389
389
  }
390
390
 
391
391
  .snackbar-item__content--outlined.snackbar-item__content--warning {
392
- background-color: var(--custom-color, var(--svelte-ui-snackbar-warning-outlined-bg));
393
- color: var(--custom-text-color, var(--svelte-ui-snackbar-warning-outlined-text-color));
392
+ background-color: var(--svelte-ui-snackbar-item-custom-color, var(--svelte-ui-snackbar-warning-outlined-bg));
393
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-warning-outlined-text-color));
394
394
  box-shadow: inset 0 0 0 1px
395
- var(--custom-color, var(--svelte-ui-snackbar-warning-outlined-border-color));
395
+ var(--svelte-ui-snackbar-item-custom-color, var(--svelte-ui-snackbar-warning-outlined-border-color));
396
396
  }
397
397
 
398
398
  .snackbar-item__content--outlined.snackbar-item__content--warning .snackbar-item__icon {
399
- color: var(--custom-text-color, var(--svelte-ui-snackbar-warning-outlined-text-color));
399
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-warning-outlined-text-color));
400
400
  }
401
401
 
402
402
  .snackbar-item__content--outlined.snackbar-item__content--error {
403
- background-color: var(--custom-color, var(--svelte-ui-snackbar-error-outlined-bg));
404
- color: var(--custom-text-color, var(--svelte-ui-snackbar-error-outlined-text-color));
403
+ background-color: var(--svelte-ui-snackbar-item-custom-color, var(--svelte-ui-snackbar-error-outlined-bg));
404
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-error-outlined-text-color));
405
405
  box-shadow: inset 0 0 0 1px
406
- var(--custom-color, var(--svelte-ui-snackbar-error-outlined-border-color));
406
+ var(--svelte-ui-snackbar-item-custom-color, var(--svelte-ui-snackbar-error-outlined-border-color));
407
407
  }
408
408
 
409
409
  .snackbar-item__content--outlined.snackbar-item__content--error .snackbar-item__icon {
410
- color: var(--custom-text-color, var(--svelte-ui-snackbar-error-outlined-text-color));
410
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-error-outlined-text-color));
411
411
  }
412
412
 
413
413
  .snackbar-item__content--outlined.snackbar-item__content--default {
414
- background-color: var(--custom-color, var(--svelte-ui-snackbar-default-outlined-bg));
415
- color: var(--custom-text-color, var(--svelte-ui-snackbar-default-outlined-text-color));
414
+ background-color: var(--svelte-ui-snackbar-item-custom-color, var(--svelte-ui-snackbar-default-outlined-bg));
415
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-default-outlined-text-color));
416
416
  box-shadow: inset 0 0 0 1px
417
- var(--custom-color, var(--svelte-ui-snackbar-default-outlined-border-color));
417
+ var(--svelte-ui-snackbar-item-custom-color, var(--svelte-ui-snackbar-default-outlined-border-color));
418
418
  }
419
419
 
420
420
  .snackbar-item__content--outlined.snackbar-item__content--default .snackbar-item__icon {
421
- color: var(--custom-text-color, var(--svelte-ui-snackbar-default-outlined-text-color));
421
+ color: var(--svelte-ui-snackbar-item-custom-text-color, var(--svelte-ui-snackbar-default-outlined-text-color));
422
422
  }
423
423
  </style>
@@ -24,6 +24,8 @@
24
24
  // 状態/動作
25
25
  disabled = false,
26
26
  required = false,
27
+
28
+ // ARIA/アクセシビリティ
27
29
  reducedMotion = false,
28
30
 
29
31
  // フォーカスイベント
@@ -59,32 +61,30 @@
59
61
  onpointermove = () => {}, // No params for type inference
60
62
  onpointercancel = () => {}, // No params for type inference
61
63
 
62
- // 変更イベント
64
+ // 入力イベント
63
65
  onchange = (value: boolean) => {},
64
66
 
65
67
  // その他
66
68
  ...restProps
67
69
  }: {
70
+ // Snippet
71
+ children?: Snippet;
72
+
68
73
  // 基本プロパティ
69
74
  value?: boolean;
70
75
 
71
- // ラベル
72
- children?: Snippet;
73
-
74
76
  // HTML属性系
75
77
  id?: string;
76
78
  inputAttributes?: HTMLInputAttributes | undefined;
77
79
 
78
- // サイズ
80
+ // スタイル/レイアウト
79
81
  size?: 'small' | 'medium' | 'large';
80
82
 
81
- // 無効状態
83
+ // 状態/動作
82
84
  disabled?: boolean;
83
-
84
- // 必須項目
85
85
  required?: boolean;
86
86
 
87
- // アニメーション無効化
87
+ // ARIA/アクセシビリティ
88
88
  reducedMotion?: boolean;
89
89
 
90
90
  // フォーカスイベント
@@ -120,7 +120,7 @@
120
120
  onpointermove?: Function; // No params for type inference
121
121
  onpointercancel?: Function; // No params for type inference
122
122
 
123
- // 変更イベント
123
+ // 入力イベント
124
124
  onchange?: (value: boolean) => void;
125
125
 
126
126
  // その他
@@ -1,8 +1,8 @@
1
1
  import type { Snippet } from 'svelte';
2
2
  import type { HTMLInputAttributes } from 'svelte/elements';
3
3
  type $$ComponentProps = {
4
- value?: boolean;
5
4
  children?: Snippet;
5
+ value?: boolean;
6
6
  id?: string;
7
7
  inputAttributes?: HTMLInputAttributes | undefined;
8
8
  size?: 'small' | 'medium' | 'large';
@@ -52,7 +52,7 @@
52
52
  href={tabItem.href}
53
53
  class="tab-item"
54
54
  class:tab-item--selected={isSelected}
55
- style="--text-color: {textColor}; --selected-text-color: {selectedTextColor}; --selected-bar-color: {selectedBarColor}"
55
+ style="--svelte-ui-tab-item-text-color: {textColor}; --svelte-ui-tab-item-selected-text-color: {selectedTextColor}; --svelte-ui-tab-item-selected-bar-color: {selectedBarColor}"
56
56
  role="tab"
57
57
  aria-selected={isSelected}
58
58
  tabindex={0}
@@ -84,7 +84,7 @@
84
84
  gap: 8px;
85
85
  position: relative;
86
86
  padding: 8px 16px;
87
- color: var(--text-color);
87
+ color: var(--svelte-ui-tab-item-text-color);
88
88
  white-space: nowrap;
89
89
  text-decoration: none;
90
90
  transition-property: background-color, color, outline;
@@ -122,7 +122,7 @@
122
122
  bottom: 0;
123
123
  width: calc(100% - 32px);
124
124
  height: 4px;
125
- background-color: var(--selected-bar-color);
125
+ background-color: var(--svelte-ui-tab-item-selected-bar-color);
126
126
  border-radius: 3px 3px 0 0;
127
127
  opacity: 0;
128
128
  transition-property: opacity;
@@ -130,7 +130,7 @@
130
130
  }
131
131
 
132
132
  .tab-item--selected {
133
- color: var(--selected-text-color);
133
+ color: var(--svelte-ui-tab-item-selected-text-color);
134
134
  background-color: transparent;
135
135
  }
136
136
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@14ch/svelte-ui",
3
3
  "description": "Modern Svelte UI components library with TypeScript support",
4
4
  "private": false,
5
- "version": "0.0.3",
5
+ "version": "0.0.4",
6
6
  "type": "module",
7
7
  "keywords": [
8
8
  "svelte",
@@ -57,7 +57,8 @@
57
57
  "test:browser": "vitest --config vitest.config.ts --run",
58
58
  "storybook": "storybook dev -p 6006",
59
59
  "build-storybook": "storybook build",
60
- "prepublishOnly": "npm run package"
60
+ "prepublishOnly": "npm run package",
61
+ "publish": "npm publish"
61
62
  },
62
63
  "devDependencies": {
63
64
  "@chromatic-com/storybook": "^4.1.1",