@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,885 @@
1
+ <!-- Input.svelte -->
2
+
3
+ <script lang="ts">
4
+ import IconButton from './IconButton.svelte';
5
+ import Icon from './Icon.svelte';
6
+ import { getStyleFromNumber } from '../utils/style';
7
+ import type { HTMLInputAttributes } from 'svelte/elements';
8
+ import type { IconVariant, IconWeight, IconGrade, IconOpticalSize } from '../types/icon';
9
+ import { t } from '../i18n';
10
+
11
+ // =========================================================================
12
+ // Props, States & Constants
13
+ // =========================================================================
14
+ let {
15
+ // 基本プロパティ
16
+ name,
17
+ value = $bindable(),
18
+ placeholder = '',
19
+
20
+ // HTML属性系
21
+ id = null,
22
+ type = 'text',
23
+ tabindex = null,
24
+ maxlength = null,
25
+ pattern = null,
26
+ min = null,
27
+ max = null,
28
+ step = null,
29
+ size = null,
30
+ autocomplete = null,
31
+ inputAttributes,
32
+
33
+ // スタイル/レイアウト
34
+ inline = false,
35
+ focusStyle = 'outline',
36
+ customStyle = '',
37
+ fullWidth = false,
38
+ width = null,
39
+ minWidth = inline ? null : 120,
40
+ maxWidth = null,
41
+ rounded = false,
42
+
43
+ // アイコン関連
44
+ rightIcon = undefined,
45
+ leftIcon = undefined,
46
+ leftIconAriaLabel = '左アイコン',
47
+ rightIconAriaLabel = '右アイコン',
48
+ iconFilled = false,
49
+ iconWeight = 300,
50
+ iconGrade = 0,
51
+ iconOpticalSize = 24,
52
+ iconVariant = 'outlined',
53
+
54
+ // 状態/動作
55
+ disabled = false,
56
+ readonly = false,
57
+ required = false,
58
+ clearable = false,
59
+ clearButtonAriaLabel = t('input.clear'),
60
+
61
+ // フォーカスイベント
62
+ onfocus = () => {}, // No params for type inference
63
+ onblur = () => {}, // No params for type inference
64
+
65
+ // キーボードイベント
66
+ onkeydown = () => {}, // No params for type inference
67
+ onkeyup = () => {}, // No params for type inference
68
+
69
+ // マウスイベント
70
+ onclick = () => {}, // No params for type inference
71
+ onmousedown = () => {}, // No params for type inference
72
+ onmouseup = () => {}, // No params for type inference
73
+ onmouseenter = () => {}, // No params for type inference
74
+ onmouseleave = () => {}, // No params for type inference
75
+ onmouseover = () => {}, // No params for type inference
76
+ onmouseout = () => {}, // No params for type inference
77
+ oncontextmenu = () => {}, // No params for type inference
78
+ onauxclick = () => {}, // No params for type inference
79
+
80
+ // タッチイベント
81
+ ontouchstart = () => {}, // No params for type inference
82
+ ontouchend = () => {}, // No params for type inference
83
+ ontouchmove = () => {}, // No params for type inference
84
+ ontouchcancel = () => {}, // No params for type inference
85
+
86
+ // ポインターイベント
87
+ onpointerdown = () => {}, // No params for type inference
88
+ onpointerup = () => {}, // No params for type inference
89
+ onpointerenter = () => {}, // No params for type inference
90
+ onpointerleave = () => {}, // No params for type inference
91
+ onpointermove = () => {}, // No params for type inference
92
+ onpointercancel = () => {}, // No params for type inference
93
+
94
+ // 入力イベント
95
+ onchange = () => {}, // No params for type inference
96
+ oninput = () => {}, // No params for type inference
97
+ onsubmit = () => {}, // No params for type inference
98
+
99
+ // IMEイベント
100
+ oncompositionstart = () => {}, // No params for type inference
101
+ oncompositionend = () => {}, // No params for type inference
102
+
103
+ // アイコンイベント
104
+ onRightIconClick,
105
+ onLeftIconClick,
106
+
107
+ // その他
108
+ ...restProps
109
+ }: {
110
+ // 基本プロパティ
111
+ name?: string;
112
+ value: string | number | undefined;
113
+
114
+ // HTML属性系
115
+ id?: string | null;
116
+ type?: 'text' | 'password' | 'email' | 'tel' | 'url' | 'number';
117
+ tabindex?: number | null;
118
+ maxlength?: number | null;
119
+ pattern?: string | null;
120
+ min?: number | null;
121
+ max?: number | null;
122
+ step?: number | null;
123
+ size?: number | null;
124
+ autocomplete?: HTMLInputElement['autocomplete'] | null;
125
+ inputAttributes?: HTMLInputAttributes | undefined;
126
+
127
+ // スタイル/レイアウト
128
+ inline?: boolean;
129
+ focusStyle?: 'background' | 'outline' | 'none';
130
+ placeholder?: string;
131
+ fullWidth?: boolean;
132
+ width?: string | number | null;
133
+ minWidth?: string | number | null;
134
+ maxWidth?: string | number | null;
135
+ rounded?: boolean;
136
+ customStyle?: string;
137
+
138
+ // アイコン関連
139
+ rightIcon?: string;
140
+ leftIcon?: string;
141
+ leftIconAriaLabel?: string;
142
+ rightIconAriaLabel?: string;
143
+ iconFilled?: boolean;
144
+ iconWeight?: IconWeight;
145
+ iconGrade?: IconGrade;
146
+ iconOpticalSize?: IconOpticalSize;
147
+ iconVariant?: IconVariant;
148
+
149
+ // 状態/動作
150
+ disabled?: boolean;
151
+ readonly?: boolean;
152
+ required?: boolean;
153
+ clearable?: boolean;
154
+
155
+ // フォーカスイベント
156
+ onfocus?: Function; // No params for type inference
157
+ onblur?: Function; // No params for type inference
158
+
159
+ // キーボードイベント
160
+ onkeydown?: Function; // No params for type inference
161
+ onkeyup?: Function; // No params for type inference
162
+
163
+ // マウスイベント
164
+ onclick?: Function; // No params for type inference
165
+ onmousedown?: Function; // No params for type inference
166
+ onmouseup?: Function; // No params for type inference
167
+ onmouseenter?: Function; // No params for type inference
168
+ onmouseleave?: Function; // No params for type inference
169
+ onmouseover?: Function; // No params for type inference
170
+ onmouseout?: Function; // No params for type inference
171
+ oncontextmenu?: Function; // No params for type inference
172
+ onauxclick?: Function; // No params for type inference
173
+
174
+ // タッチイベント
175
+ ontouchstart?: Function; // No params for type inference
176
+ ontouchend?: Function; // No params for type inference
177
+ ontouchmove?: Function; // No params for type inference
178
+ ontouchcancel?: Function; // No params for type inference
179
+
180
+ // ポインターイベント
181
+ onpointerdown?: Function; // No params for type inference
182
+ onpointerup?: Function; // No params for type inference
183
+ onpointerenter?: Function; // No params for type inference
184
+ onpointerleave?: Function; // No params for type inference
185
+ onpointermove?: Function; // No params for type inference
186
+ onpointercancel?: Function; // No params for type inference
187
+
188
+ // 入力イベント
189
+ onchange?: (value: any) => void;
190
+ oninput?: (value: any) => void;
191
+ onsubmit?: (value: any) => void;
192
+
193
+ // IMEイベント
194
+ oncompositionstart?: Function; // No params for type inference
195
+ oncompositionend?: Function; // No params for type inference
196
+
197
+ // アイコンイベント
198
+ onRightIconClick?: Function; // No params for type inference
199
+ onLeftIconClick?: Function; // No params for type inference
200
+
201
+ // その他
202
+ [key: string]: any;
203
+ } = $props();
204
+
205
+ let ref: HTMLInputElement | undefined = $state();
206
+ let isFocused: boolean = $state(false);
207
+
208
+ // =========================================================================
209
+ // Methods
210
+ // =========================================================================
211
+ // ユーティリティ
212
+ const clear = (): void => {
213
+ if (disabled || readonly) return;
214
+ ref?.focus();
215
+ onchange?.(undefined);
216
+ value = undefined;
217
+ };
218
+
219
+ export const focus = () => {
220
+ if (ref) {
221
+ ref.focus();
222
+ if (type !== 'number') {
223
+ ref.setSelectionRange?.(0, 0);
224
+ }
225
+ ref.scrollTop = 0;
226
+ }
227
+ };
228
+
229
+ // フォーカスイベント
230
+ const handleFocus = (event: FocusEvent) => {
231
+ if (disabled) return;
232
+ isFocused = true;
233
+ onfocus?.(event);
234
+ };
235
+
236
+ const handleBlur = (event: FocusEvent) => {
237
+ if (disabled) return;
238
+ isFocused = false;
239
+ onblur?.(event);
240
+ };
241
+
242
+ // キーボードイベント
243
+ const handleKeydown = (event: KeyboardEvent) => {
244
+ onkeydown?.(event);
245
+ };
246
+
247
+ const handleKeyup = (event: KeyboardEvent) => {
248
+ onkeyup?.(event);
249
+ };
250
+
251
+ // 入力イベント
252
+ const handleSubmit = (event: SubmitEvent) => {
253
+ if (disabled || readonly) return;
254
+ event?.preventDefault?.();
255
+ ref?.blur();
256
+ onsubmit?.(value);
257
+ };
258
+
259
+ const handleChange = () => {
260
+ if (disabled || readonly) return;
261
+ onchange?.(value);
262
+ };
263
+
264
+ const handleInput = () => {
265
+ if (disabled || readonly) return;
266
+ oninput?.(value);
267
+ };
268
+
269
+ // マウスイベント
270
+ const handleClick = (event: MouseEvent) => {
271
+ if (disabled) return;
272
+ onclick?.(event);
273
+ };
274
+
275
+ const handleMouseDown = (event: MouseEvent) => {
276
+ if (disabled) return;
277
+ onmousedown?.(event);
278
+ };
279
+
280
+ const handleMouseUp = (event: MouseEvent) => {
281
+ if (disabled) return;
282
+ onmouseup?.(event);
283
+ };
284
+
285
+ const handleMouseEnter = (event: MouseEvent) => {
286
+ if (disabled) return;
287
+ onmouseenter?.(event);
288
+ };
289
+
290
+ const handleMouseLeave = (event: MouseEvent) => {
291
+ if (disabled) return;
292
+ onmouseleave?.(event);
293
+ };
294
+
295
+ const handleMouseOver = (event: MouseEvent) => {
296
+ if (disabled) return;
297
+ onmouseover?.(event);
298
+ };
299
+
300
+ const handleMouseOut = (event: MouseEvent) => {
301
+ if (disabled) return;
302
+ onmouseout?.(event);
303
+ };
304
+
305
+ const handleContextMenu = (event: MouseEvent) => {
306
+ if (disabled) return;
307
+ oncontextmenu?.(event);
308
+ };
309
+
310
+ const handleAuxClick = (event: MouseEvent) => {
311
+ if (disabled) return;
312
+ onauxclick?.(event);
313
+ };
314
+
315
+ // タッチイベント
316
+ const handleTouchStart = (event: TouchEvent) => {
317
+ if (disabled) return;
318
+ ontouchstart?.(event);
319
+ };
320
+
321
+ const handleTouchEnd = (event: TouchEvent) => {
322
+ if (disabled) return;
323
+ ontouchend?.(event);
324
+ };
325
+
326
+ const handleTouchMove = (event: TouchEvent) => {
327
+ if (disabled) return;
328
+ ontouchmove?.(event);
329
+ };
330
+
331
+ const handleTouchCancel = (event: TouchEvent) => {
332
+ if (disabled) return;
333
+ ontouchcancel?.(event);
334
+ };
335
+
336
+ // ポインターイベント
337
+ const handlePointerDown = (event: PointerEvent) => {
338
+ if (disabled) return;
339
+ onpointerdown?.(event);
340
+ };
341
+
342
+ const handlePointerUp = (event: PointerEvent) => {
343
+ if (disabled) return;
344
+ onpointerup?.(event);
345
+ };
346
+
347
+ const handlePointerEnter = (event: PointerEvent) => {
348
+ if (disabled) return;
349
+ onpointerenter?.(event);
350
+ };
351
+
352
+ const handlePointerLeave = (event: PointerEvent) => {
353
+ if (disabled) return;
354
+ onpointerleave?.(event);
355
+ };
356
+
357
+ const handlePointerMove = (event: PointerEvent) => {
358
+ if (disabled) return;
359
+ onpointermove?.(event);
360
+ };
361
+
362
+ const handlePointerCancel = (event: PointerEvent) => {
363
+ if (disabled) return;
364
+ onpointercancel?.(event);
365
+ };
366
+
367
+ // =========================================================================
368
+ // $derived
369
+ // =========================================================================
370
+ const getDisplayValue = (): string => {
371
+ if (value === undefined) return '';
372
+ if (type === 'number' && typeof value === 'number') {
373
+ return value.toLocaleString();
374
+ }
375
+ return String(value);
376
+ };
377
+
378
+ const widthStyle = $derived(getStyleFromNumber(width));
379
+ const maxWidthStyle = $derived(getStyleFromNumber(maxWidth));
380
+ const minWidthStyle = $derived(getStyleFromNumber(minWidth));
381
+ </script>
382
+
383
+ <div
384
+ class="input
385
+ input--focus-{focusStyle}
386
+ input--type-{type}"
387
+ class:input--inline={inline}
388
+ class:input--auto-resize={inline}
389
+ class:input--full-width={fullWidth}
390
+ class:input--clearable={clearable}
391
+ class:input--has-right-icon={!!rightIcon}
392
+ class:input--has-left-icon={!!leftIcon}
393
+ class:input--has-left-icon-clickable={!!leftIcon && !!onLeftIconClick}
394
+ class:input--rounded={rounded}
395
+ class:input--disabled={disabled}
396
+ class:input--readonly={readonly}
397
+ class:input--focused={isFocused}
398
+ data-testid="input"
399
+ style="width: {widthStyle}; max-width: {maxWidthStyle}; min-width: {minWidthStyle}"
400
+ >
401
+ <!-- inline時の表示用要素(text-overflow: ellipsisが効く) -->
402
+ {#if inline}
403
+ <div class="input__display-text" data-placeholder={placeholder} style={customStyle}>
404
+ {getDisplayValue()}
405
+ </div>
406
+ {/if}
407
+ <!-- 入力用フォーム -->
408
+ <form onsubmit={handleSubmit}>
409
+ <input
410
+ {id}
411
+ {name}
412
+ bind:value
413
+ bind:this={ref}
414
+ {type}
415
+ style={customStyle}
416
+ {placeholder}
417
+ {autocomplete}
418
+ {min}
419
+ {max}
420
+ {step}
421
+ {maxlength}
422
+ {pattern}
423
+ {disabled}
424
+ {readonly}
425
+ {required}
426
+ {tabindex}
427
+ {size}
428
+ onchange={handleChange}
429
+ oninput={handleInput}
430
+ onfocus={handleFocus}
431
+ onblur={handleBlur}
432
+ onkeydown={handleKeydown}
433
+ onkeyup={handleKeyup}
434
+ onclick={handleClick}
435
+ onmousedown={handleMouseDown}
436
+ onmouseup={handleMouseUp}
437
+ onmouseenter={handleMouseEnter}
438
+ onmouseleave={handleMouseLeave}
439
+ onmouseover={handleMouseOver}
440
+ onmouseout={handleMouseOut}
441
+ oncontextmenu={handleContextMenu}
442
+ onauxclick={handleAuxClick}
443
+ ontouchstart={handleTouchStart}
444
+ ontouchend={handleTouchEnd}
445
+ ontouchmove={handleTouchMove}
446
+ ontouchcancel={handleTouchCancel}
447
+ onpointerdown={handlePointerDown}
448
+ onpointerup={handlePointerUp}
449
+ onpointerenter={handlePointerEnter}
450
+ onpointerleave={handlePointerLeave}
451
+ onpointermove={handlePointerMove}
452
+ onpointercancel={handlePointerCancel}
453
+ {...inputAttributes}
454
+ {...restProps}
455
+ />
456
+ </form>
457
+ <!-- クリアボタン -->
458
+ {#if clearable && !disabled && !readonly}
459
+ <div class="input__clear-button">
460
+ <IconButton
461
+ ariaLabel={t('input.clear')}
462
+ color="var(--svelte-ui-input-text-color)"
463
+ onclick={(event) => {
464
+ event.stopPropagation();
465
+ clear();
466
+ }}
467
+ tabindex={-1}
468
+ iconFilled={true}
469
+ fontSize={18}>cancel</IconButton
470
+ >
471
+ </div>
472
+ {/if}
473
+
474
+ <!-- Left Icon -->
475
+ {#if leftIcon}
476
+ <div class="input__icon-left">
477
+ {#if onLeftIconClick}
478
+ <IconButton
479
+ ariaLabel={leftIconAriaLabel}
480
+ color="var(--svelte-ui-input-icon-color)"
481
+ onclick={onLeftIconClick}
482
+ tabindex={-1}
483
+ {iconFilled}
484
+ {iconWeight}
485
+ >
486
+ {leftIcon}
487
+ </IconButton>
488
+ {:else}
489
+ <div class="input__normal-icon">
490
+ <Icon
491
+ filled={iconFilled}
492
+ weight={iconWeight}
493
+ grade={iconGrade}
494
+ opticalSize={iconOpticalSize}
495
+ variant={iconVariant}
496
+ >
497
+ {leftIcon}
498
+ </Icon>
499
+ </div>
500
+ {/if}
501
+ </div>
502
+ {/if}
503
+
504
+ <!-- Right Icon -->
505
+ {#if rightIcon}
506
+ <div class="input__icon-right">
507
+ {#if onRightIconClick}
508
+ <IconButton
509
+ ariaLabel={rightIconAriaLabel}
510
+ color="var(--svelte-ui-input-icon-color)"
511
+ onclick={onRightIconClick}
512
+ tabindex={-1}
513
+ {iconFilled}
514
+ {iconWeight}
515
+ >
516
+ {rightIcon}
517
+ </IconButton>
518
+ {:else}
519
+ <div class="input__normal-icon">
520
+ <Icon
521
+ filled={iconFilled}
522
+ weight={iconWeight}
523
+ grade={iconGrade}
524
+ opticalSize={iconOpticalSize}
525
+ variant={iconVariant}
526
+ >
527
+ {rightIcon}
528
+ </Icon>
529
+ </div>
530
+ {/if}
531
+ </div>
532
+ {/if}
533
+ </div>
534
+
535
+ <style>
536
+ /* =============================================
537
+ * 基本構造・レイアウト
538
+ * ============================================= */
539
+ .input {
540
+ display: inline-block;
541
+ position: relative;
542
+ width: auto;
543
+ max-width: 100%;
544
+ height: inherit;
545
+ }
546
+
547
+ form {
548
+ padding: inherit;
549
+ border: none;
550
+ font-size: inherit;
551
+ font-weight: inherit;
552
+ color: inherit;
553
+ line-height: inherit;
554
+ text-align: inherit;
555
+ opacity: 0;
556
+ }
557
+
558
+ /* =============================================
559
+ * 基本コンポーネント
560
+ * ============================================= */
561
+ input {
562
+ width: 100%;
563
+ min-width: 1em;
564
+ padding: inherit;
565
+ background: transparent;
566
+ border: none;
567
+ border-radius: 0;
568
+ font-size: inherit;
569
+ font-weight: inherit;
570
+ color: inherit;
571
+ line-height: inherit;
572
+ text-align: inherit;
573
+
574
+ &[type='number'] {
575
+ text-align: right;
576
+ &::-webkit-outer-spin-button,
577
+ &::-webkit-inner-spin-button {
578
+ -webkit-appearance: none;
579
+ margin: 0;
580
+ }
581
+ }
582
+ }
583
+
584
+ .input__display-text {
585
+ display: inline-block;
586
+ vertical-align: top;
587
+ width: 100%;
588
+ min-width: 1em;
589
+ padding: inherit;
590
+ background: inherit;
591
+ border: inherit;
592
+ font-size: inherit;
593
+ font-weight: inherit;
594
+ color: inherit;
595
+ line-height: inherit;
596
+ text-align: inherit;
597
+ white-space: nowrap;
598
+ vertical-align: top;
599
+ overflow: hidden;
600
+ text-overflow: ellipsis;
601
+ opacity: 1;
602
+ transition: none;
603
+ cursor: text !important;
604
+
605
+ &::before {
606
+ content: '';
607
+ }
608
+
609
+ &:empty::before {
610
+ content: attr(data-placeholder);
611
+ }
612
+ }
613
+
614
+ .input__clear-button {
615
+ position: absolute;
616
+ top: 50%;
617
+ right: 4px;
618
+ width: 32px;
619
+ height: 32px;
620
+ transform: translate(0, -50%);
621
+ opacity: 0;
622
+ transition: var(--svelte-ui-transition-duration);
623
+ }
624
+
625
+ /* rightIconがある場合はクリアボタンを左にずらす */
626
+ .input--clearable.input--has-right-icon .input__clear-button {
627
+ right: var(--svelte-ui-input-icon-space);
628
+ }
629
+
630
+ @media (hover: hover) {
631
+ :hover .input__clear-button {
632
+ opacity: 1;
633
+ }
634
+ }
635
+
636
+ @media (hover: none) {
637
+ .input__clear-button {
638
+ opacity: 1;
639
+ }
640
+ }
641
+
642
+ .input__icon-left {
643
+ display: flex;
644
+ justify-content: center;
645
+ align-items: center;
646
+ position: absolute;
647
+ top: 50%;
648
+ left: 4px;
649
+ width: 32px;
650
+ height: 32px;
651
+ transform: translateY(-50%);
652
+ font-size: var(--svelte-ui-select-dropdown-icon-size);
653
+ color: var(--svelte-ui-input-icon-color);
654
+ pointer-events: none;
655
+ z-index: 1;
656
+ }
657
+
658
+ /* 左側アイコンにクリックハンドラーがある場合はクリック可能にする */
659
+ .input--has-left-icon-clickable .input__icon-left {
660
+ pointer-events: auto;
661
+ cursor: pointer;
662
+ }
663
+
664
+ .input__icon-right {
665
+ display: flex;
666
+ justify-content: center;
667
+ align-items: center;
668
+ position: absolute;
669
+ top: 50%;
670
+ right: 4px;
671
+ width: 32px;
672
+ height: 32px;
673
+ transform: translateY(-50%);
674
+ font-size: var(--svelte-ui-select-dropdown-icon-size);
675
+ color: var(--svelte-ui-input-icon-color);
676
+ pointer-events: none;
677
+ z-index: 1;
678
+ }
679
+
680
+ /* 通常のアイコン(ボタンではない)はクリック不可 */
681
+ .input__normal-icon {
682
+ pointer-events: none;
683
+ }
684
+
685
+ /* =============================================
686
+ * デザインバリアント:default
687
+ * ============================================= */
688
+ .input:not(.input--inline) {
689
+ form {
690
+ position: static;
691
+ opacity: 1;
692
+ }
693
+
694
+ input {
695
+ min-height: var(--svelte-ui-input-height);
696
+ padding: var(--svelte-ui-input-padding);
697
+ background-color: var(--svelte-ui-input-bg);
698
+ box-shadow: 0 0 0 var(--svelte-ui-border-width) inset var(--svelte-ui-input-border-color);
699
+ border: none;
700
+ border-radius: var(--svelte-ui-input-border-radius);
701
+ color: var(--svelte-ui-input-text-color);
702
+ }
703
+
704
+ &.input--has-left-icon {
705
+ input,
706
+ .input__display-text {
707
+ padding-left: var(--svelte-ui-input-icon-space);
708
+ }
709
+ }
710
+
711
+ &.input--has-right-icon {
712
+ input,
713
+ .input__display-text {
714
+ padding-right: var(--svelte-ui-input-icon-space);
715
+ }
716
+ }
717
+
718
+ &.input--clearable {
719
+ input,
720
+ .input__display-text {
721
+ padding-right: var(--svelte-ui-input-icon-space);
722
+ }
723
+ }
724
+
725
+ &.input--clearable.input--has-right-icon {
726
+ input,
727
+ .input__display-text {
728
+ padding-right: var(--svelte-ui-input-icon-space-double);
729
+ }
730
+ }
731
+ }
732
+
733
+ /* =============================================
734
+ * デザインバリアント:rounded
735
+ * ============================================= */
736
+ .input--rounded:not(.input--inline) {
737
+ input {
738
+ border-radius: var(--svelte-ui-input-border-radius-rounded);
739
+ }
740
+ }
741
+
742
+ /* =============================================
743
+ * デザインバリアント:inline
744
+ * ============================================= */
745
+ .input--inline {
746
+ &.input--type-number .input__display-text {
747
+ text-align: right;
748
+ }
749
+
750
+ form {
751
+ position: absolute;
752
+ top: 0;
753
+ left: 0;
754
+ width: 100%;
755
+ height: 100%;
756
+ }
757
+
758
+ &.input--has-left-icon {
759
+ input,
760
+ .input__display-text {
761
+ padding-left: var(--svelte-ui-input-icon-space-inline);
762
+ }
763
+ }
764
+
765
+ &.input--has-right-icon {
766
+ input,
767
+ .input__display-text {
768
+ padding-right: var(--svelte-ui-input-icon-space-inline);
769
+ }
770
+ }
771
+
772
+ &.input--clearable {
773
+ input,
774
+ .input__display-text {
775
+ padding-right: var(--svelte-ui-input-icon-space-inline);
776
+ }
777
+ }
778
+
779
+ &.input--clearable.input--has-right-icon {
780
+ input,
781
+ .input__display-text {
782
+ padding-right: var(--svelte-ui-input-icon-space-double-inline);
783
+ }
784
+ }
785
+
786
+ &.input--focused {
787
+ .input__display-text {
788
+ opacity: 0;
789
+ }
790
+
791
+ form {
792
+ opacity: 1;
793
+ }
794
+ }
795
+ }
796
+
797
+ /* =============================================
798
+ * レイアウトバリエーション
799
+ * ============================================= */
800
+ .input--auto-resize {
801
+ width: fit-content;
802
+ }
803
+
804
+ .input--full-width {
805
+ width: 100%;
806
+ }
807
+
808
+ /* =============================================
809
+ * 機能バリエーション
810
+ * ============================================= */
811
+ .input--clearable {
812
+ input,
813
+ .input__display-text {
814
+ padding-right: var(--svelte-ui-input-icon-space);
815
+ }
816
+ }
817
+
818
+ .input.input--has-right-icon {
819
+ input,
820
+ .input__display-text {
821
+ padding-right: var(--svelte-ui-input-icon-space);
822
+ }
823
+ }
824
+
825
+ .input.input--has-left-icon {
826
+ input,
827
+ .input__display-text {
828
+ padding-left: var(--svelte-ui-input-icon-space);
829
+ }
830
+ }
831
+
832
+ /* =============================================
833
+ * プレースホルダー・テキスト表示
834
+ * ============================================= */
835
+ input::placeholder,
836
+ .input__display-text:empty::before {
837
+ color: var(--svelte-ui-input-placeholder-color);
838
+ }
839
+
840
+ /* =============================================
841
+ * フォーカス効果バリエーション
842
+ * ============================================= */
843
+ .input--focus-outline input:focus {
844
+ outline: var(--svelte-ui-focus-outline-inner);
845
+ outline-offset: var(--svelte-ui-focus-outline-offset-inner);
846
+ }
847
+
848
+ .input--focus-background input:focus {
849
+ background: var(--svelte-ui-hover-overlay);
850
+ outline: none;
851
+ }
852
+
853
+ .input--focus-none input:focus {
854
+ outline: none;
855
+ }
856
+
857
+ /* =============================================
858
+ * 状態管理(disabled, readonly等)
859
+ * ============================================= */
860
+ .input--disabled {
861
+ opacity: var(--svelte-ui-input-disabled-opacity);
862
+ pointer-events: none;
863
+
864
+ .input__icon-left,
865
+ .input__icon-right {
866
+ opacity: var(--svelte-ui-button-disabled-opacity);
867
+ }
868
+ }
869
+
870
+ .input--readonly {
871
+ input {
872
+ cursor: default;
873
+ }
874
+ }
875
+
876
+ input:disabled {
877
+ opacity: var(--svelte-ui-button-disabled-opacity);
878
+ cursor: not-allowed;
879
+ }
880
+
881
+ input[readonly] {
882
+ background-color: var(--svelte-ui-input-readonly-bg);
883
+ cursor: default;
884
+ }
885
+ </style>