@14ch/svelte-ui 0.0.69 → 0.0.71

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.
@@ -385,7 +385,6 @@
385
385
  class:color-picker--readonly={readonly}
386
386
  class:color-picker--focused={isFocused}
387
387
  data-testid="color-picker"
388
- style={customStyle}
389
388
  >
390
389
  <Input
391
390
  id={id ? `${id}-input` : undefined}
@@ -402,7 +401,7 @@
402
401
  {minWidth}
403
402
  {maxWidth}
404
403
  {rounded}
405
- customStyle={`padding-left: var(--svelte-ui-colorpicker-text-padding-left); ${customStyle}`}
404
+ customStyle={`${customStyle}; padding-left: var(--svelte-ui-colorpicker-text-padding-left)`}
406
405
  onchange={handleChange}
407
406
  oninput={handleInput}
408
407
  onfocus={handleFocus}
@@ -704,9 +704,37 @@
704
704
  // --------------------------------
705
705
  // style from number
706
706
  // --------------------------------
707
+ // アイコンの領域は customStyle で消されないよう、customStyle より後ろに指定する
708
+ const iconSpaceSuffix = $derived(inline ? '-inline' : '');
709
+ const iconPaddingStyle = $derived(
710
+ [
711
+ hasLeftIcon ? `padding-left: var(--svelte-ui-input-icon-space${iconSpaceSuffix})` : '',
712
+ clearable && hasRightIcon
713
+ ? `padding-right: var(--svelte-ui-input-icon-space-double${iconSpaceSuffix})`
714
+ : clearable || hasRightIcon
715
+ ? `padding-right: var(--svelte-ui-input-icon-space${iconSpaceSuffix})`
716
+ : ''
717
+ ]
718
+ .filter(Boolean)
719
+ .join('; ')
720
+ );
707
721
  const widthStyle = $derived(getStyleFromNumber(width));
708
722
  const maxWidthStyle = $derived(getStyleFromNumber(maxWidth));
709
723
  const minWidthStyle = $derived(getStyleFromNumber(minWidth));
724
+
725
+ // style: ディレクティブは style 属性に勝つため、幅の指定も customStyle と同じ文字列に混ぜる。
726
+ // 専用プロップが指定されたときだけ customStyle より後ろに置いてプロップを優先させる
727
+ const boxStyle = $derived(
728
+ [
729
+ customStyle,
730
+ iconPaddingStyle,
731
+ widthStyle ? `width: ${widthStyle}` : '',
732
+ minWidthStyle ? `min-width: ${minWidthStyle}` : '',
733
+ maxWidthStyle ? `max-width: ${maxWidthStyle}` : ''
734
+ ]
735
+ .filter(Boolean)
736
+ .join('; ')
737
+ );
710
738
  </script>
711
739
 
712
740
  <div
@@ -727,12 +755,10 @@
727
755
  class:input--readonly={readonly}
728
756
  class:input--focused={isFocused}
729
757
  data-testid="input"
730
- style:width={widthStyle}
731
- style:max-width={maxWidthStyle}
732
- style:min-width={minWidthStyle}
758
+ style={boxStyle}
733
759
  >
734
760
  <!-- 表示用テキスト -->
735
- <div class="input__display-text" style={customStyle}>
761
+ <div class="input__display-text">
736
762
  <div
737
763
  class="input__display-text-content"
738
764
  class:input__display-text-content--placeholder={!hasDisplayValue}
@@ -753,7 +779,6 @@
753
779
  bind:value
754
780
  bind:this={ref}
755
781
  type={resolvedType}
756
- style={customStyle}
757
782
  {placeholder}
758
783
  {autocomplete}
759
784
  {min}
@@ -798,7 +823,7 @@
798
823
  />
799
824
  </div>
800
825
  {#if isLinkifyActive}
801
- <div class="input__link-text" style={customStyle}>
826
+ <div class="input__link-text">
802
827
  <div class="input__link-text-content">
803
828
  {@html linkHtmlValue}
804
829
  </div>
@@ -905,6 +930,11 @@
905
930
  width: auto;
906
931
  max-width: 100%;
907
932
  height: inherit;
933
+ min-height: var(--svelte-ui-input-height);
934
+ padding: var(--svelte-ui-input-padding);
935
+ background-color: var(--svelte-ui-input-bg);
936
+ box-shadow: 0 0 0 var(--svelte-ui-border-width) inset var(--svelte-ui-input-border-color);
937
+ border-radius: var(--svelte-ui-input-border-radius);
908
938
  }
909
939
 
910
940
  .input__wrapper {
@@ -915,9 +945,8 @@
915
945
  height: 100%;
916
946
  padding: inherit;
917
947
  border: none;
918
- border-radius: var(--svelte-ui-input-border-radius);
919
- background-color: var(--svelte-ui-input-bg);
920
- box-shadow: 0 0 0 var(--svelte-ui-border-width) inset var(--svelte-ui-input-border-color);
948
+ border-radius: inherit;
949
+ background-color: transparent;
921
950
  font-size: inherit;
922
951
  font-weight: inherit;
923
952
  color: inherit;
@@ -935,10 +964,10 @@
935
964
  width: 100%;
936
965
  min-width: 1em;
937
966
  min-height: var(--svelte-ui-input-height);
938
- padding: var(--svelte-ui-input-padding);
967
+ padding: 0;
939
968
  background-color: transparent;
940
969
  border: none;
941
- border-radius: var(--svelte-ui-input-border-radius);
970
+ border-radius: inherit;
942
971
  font-family: inherit;
943
972
  font-size: inherit;
944
973
  font-weight: inherit;
@@ -962,7 +991,7 @@
962
991
  width: 100%;
963
992
  min-width: 1em;
964
993
  height: var(--svelte-ui-input-height);
965
- padding: var(--svelte-ui-input-padding);
994
+ padding: 0;
966
995
  font-size: inherit;
967
996
  font-weight: inherit;
968
997
  color: inherit;
@@ -990,6 +1019,7 @@
990
1019
  left: 0;
991
1020
  width: 100%;
992
1021
  height: 100%;
1022
+ padding: inherit;
993
1023
  pointer-events: none;
994
1024
  z-index: 1;
995
1025
  }
@@ -1005,7 +1035,7 @@
1005
1035
  /* =============================================
1006
1036
  * 表示制御
1007
1037
  * ============================================= */
1008
- /* 非フォーカス時: input要素自体を不可視化(枠線・背景はラッパーにあるため display-text のみ表示) */
1038
+ /* 非フォーカス時: input要素自体を不可視化(枠線・背景はルートにあるため display-text のみ表示) */
1009
1039
  .input:not(.input--focused) input {
1010
1040
  opacity: 0;
1011
1041
  }
@@ -1027,7 +1057,11 @@
1027
1057
  /* =============================================
1028
1058
  * フォーカス効果バリエーション
1029
1059
  * ============================================= */
1030
- .input--focus-outline input:focus {
1060
+ input:focus {
1061
+ outline: none;
1062
+ }
1063
+
1064
+ .input--focus-outline.input--focused .input__wrapper {
1031
1065
  outline: var(--svelte-ui-focus-outline-inner);
1032
1066
  outline-offset: var(--svelte-ui-focus-outline-offset-inner);
1033
1067
  }
@@ -1036,14 +1070,6 @@
1036
1070
  background: var(--svelte-ui-hover-overlay);
1037
1071
  }
1038
1072
 
1039
- .input--focus-background input:focus {
1040
- outline: none;
1041
- }
1042
-
1043
- .input--focus-none input:focus {
1044
- outline: none;
1045
- }
1046
-
1047
1073
  /* =============================================
1048
1074
  * 状態管理(disabled, readonly等)
1049
1075
  * ============================================= */
@@ -1078,33 +1104,21 @@
1078
1104
  * 機能バリエーション
1079
1105
  * ============================================= */
1080
1106
  /* clearable */
1107
+ /* 装飾はテキストと同じ content box を基準に中央寄せする(padding-block: inherit)。
1108
+ 絶対配置の基準はルートの padding box なので、そのままだと padding が上下非対称なときにズレる */
1081
1109
  .input__clear-button {
1110
+ display: flex;
1111
+ justify-content: center;
1112
+ align-items: center;
1082
1113
  position: absolute;
1083
- top: 50%;
1114
+ inset-block: 0;
1084
1115
  right: 4px;
1085
1116
  width: 32px;
1086
- height: 32px;
1087
- transform: translate(0, -50%);
1117
+ padding-block: inherit;
1088
1118
  opacity: 0;
1089
1119
  transition: var(--svelte-ui-transition-duration);
1090
1120
  }
1091
1121
 
1092
- .input--clearable {
1093
- input,
1094
- .input__display-text,
1095
- .input__link-text {
1096
- padding-right: var(--svelte-ui-input-icon-space);
1097
- }
1098
- }
1099
-
1100
- .input--clearable.input--has-right-icon {
1101
- input,
1102
- .input__display-text,
1103
- .input__link-text {
1104
- padding-right: var(--svelte-ui-input-icon-space-double);
1105
- }
1106
- }
1107
-
1108
1122
  .input--has-right-icon .input__clear-button {
1109
1123
  right: var(--svelte-ui-input-icon-space);
1110
1124
  }
@@ -1122,32 +1136,15 @@
1122
1136
  }
1123
1137
 
1124
1138
  /* アイコン */
1125
- .input--has-left-icon {
1126
- input,
1127
- .input__display-text,
1128
- .input__link-text {
1129
- padding-left: var(--svelte-ui-input-icon-space);
1130
- }
1131
- }
1132
-
1133
- .input--has-right-icon {
1134
- input,
1135
- .input__display-text,
1136
- .input__link-text {
1137
- padding-right: var(--svelte-ui-input-icon-space);
1138
- }
1139
- }
1140
-
1141
1139
  .input__icon-left {
1142
1140
  display: flex;
1143
1141
  justify-content: center;
1144
1142
  align-items: center;
1145
1143
  position: absolute;
1146
- top: 50%;
1144
+ inset-block: 0;
1147
1145
  left: 4px;
1148
1146
  width: 32px;
1149
- height: 32px;
1150
- transform: translateY(-50%);
1147
+ padding-block: inherit;
1151
1148
  color: var(--svelte-ui-input-icon-color);
1152
1149
  pointer-events: none;
1153
1150
  z-index: 1;
@@ -1158,11 +1155,10 @@
1158
1155
  justify-content: center;
1159
1156
  align-items: center;
1160
1157
  position: absolute;
1161
- top: 50%;
1158
+ inset-block: 0;
1162
1159
  right: 4px;
1163
1160
  width: 32px;
1164
- height: 32px;
1165
- transform: translateY(-50%);
1161
+ padding-block: inherit;
1166
1162
  color: var(--svelte-ui-input-icon-color);
1167
1163
  pointer-events: none;
1168
1164
  z-index: 2;
@@ -1214,29 +1210,27 @@
1214
1210
  * デザインバリエーション
1215
1211
  * ============================================= */
1216
1212
  /* rounded */
1217
- .input--rounded .input__wrapper,
1218
- .input--rounded input {
1213
+ .input--rounded {
1219
1214
  border-radius: var(--svelte-ui-input-border-radius-rounded);
1220
1215
  }
1221
1216
 
1222
1217
  /* inline */
1223
- .input--inline .input__wrapper,
1224
- .input--inline input {
1225
- min-height: inherit;
1226
- line-height: inherit;
1218
+ .input--inline {
1219
+ min-height: 0;
1220
+ padding: 0;
1227
1221
  background-color: transparent;
1228
1222
  box-shadow: none;
1229
- border: inherit;
1230
1223
  border-radius: 0;
1231
- }
1232
1224
 
1233
- .input--inline {
1225
+ input {
1226
+ min-height: 0;
1227
+ }
1228
+
1234
1229
  input,
1235
1230
  .input__display-text,
1236
1231
  .input__link-text {
1237
1232
  height: inherit;
1238
1233
  line-height: inherit;
1239
- padding: inherit;
1240
1234
  }
1241
1235
 
1242
1236
  /*
@@ -1249,41 +1243,11 @@
1249
1243
  line-height: normal;
1250
1244
  }
1251
1245
 
1252
- &.input--has-left-icon {
1253
- input,
1254
- .input__display-text,
1255
- .input__link-text {
1256
- padding-left: var(--svelte-ui-input-icon-space-inline);
1257
- }
1258
- }
1259
-
1260
1246
  &.input--has-right-icon {
1261
- input,
1262
- .input__display-text,
1263
- .input__link-text {
1264
- padding-right: var(--svelte-ui-input-icon-space-inline);
1265
- }
1266
-
1267
1247
  .input__icon-right {
1268
1248
  right: 0;
1269
1249
  }
1270
1250
  }
1271
-
1272
- &.input--clearable {
1273
- input,
1274
- .input__display-text,
1275
- .input__link-text {
1276
- padding-right: var(--svelte-ui-input-icon-space-inline);
1277
- }
1278
- }
1279
-
1280
- &.input--clearable.input--has-right-icon {
1281
- input,
1282
- .input__display-text,
1283
- .input__link-text {
1284
- padding-right: var(--svelte-ui-input-icon-space-double-inline);
1285
- }
1286
- }
1287
1251
  }
1288
1252
 
1289
1253
  /* full-width */
@@ -82,6 +82,13 @@
82
82
  // =========================================================================
83
83
  // $derived
84
84
  // =========================================================================
85
+ // アイコンの領域は customStyle で消されないよう、customStyle より後ろに指定する
86
+ const triggerStyle = $derived(
87
+ [customStyle, `padding-right: var(--svelte-ui-select-icon-space${inline ? '-inline' : ''})`]
88
+ .filter(Boolean)
89
+ .join('; ')
90
+ );
91
+
85
92
  const fallbackId = `multi-select-${Math.random().toString(36).substring(2, 15)}`;
86
93
  const listboxId = $derived(`${id ?? fallbackId}-listbox`);
87
94
 
@@ -161,7 +168,7 @@
161
168
  aria-required={required ? 'true' : undefined}
162
169
  {tabindex}
163
170
  {disabled}
164
- style={customStyle}
171
+ style={triggerStyle}
165
172
  onfocus={handleFocus}
166
173
  onblur={handleBlur}
167
174
  onclick={handleTriggerClick}
@@ -179,9 +186,9 @@
179
186
  {:else if placeholder}
180
187
  <span class="multi-select__placeholder">{placeholder}</span>
181
188
  {/if}
182
- <span class="multi-select__dropdown-icon" aria-hidden="true">
183
- <Icon>arrow_drop_down</Icon>
184
- </span>
189
+ </span>
190
+ <span class="multi-select__dropdown-icon" aria-hidden="true">
191
+ <Icon>arrow_drop_down</Icon>
185
192
  </span>
186
193
  </button>
187
194
 
@@ -240,10 +247,10 @@
240
247
  * トリガーボタン
241
248
  * ============================================= */
242
249
  .multi-select__trigger {
250
+ position: relative;
243
251
  width: 100%;
244
252
  min-height: var(--svelte-ui-select-height);
245
253
  padding: var(--svelte-ui-select-padding);
246
- padding-right: var(--svelte-ui-select-icon-space);
247
254
  background: transparent;
248
255
  border: none;
249
256
  font-family: inherit;
@@ -258,7 +265,6 @@
258
265
  }
259
266
 
260
267
  .multi-select__inner {
261
- position: relative;
262
268
  flex: 1;
263
269
  align-self: stretch;
264
270
  display: flex;
@@ -282,18 +288,17 @@
282
288
  flex: 1;
283
289
  }
284
290
 
291
+ /* アイコンはテキストと同じ content box を基準に中央寄せする(padding-block: inherit)。
292
+ 絶対配置の基準はトリガーの padding box なので、そのままだと padding が上下非対称なときにズレる */
285
293
  .multi-select__dropdown-icon {
286
294
  display: flex;
287
295
  justify-content: center;
288
296
  align-items: center;
289
297
  position: absolute;
290
- top: 0;
298
+ inset-block: 0;
291
299
  right: 4px;
292
- margin-top: calc((var(--svelte-ui-select-height) - 32px) / 2);
293
- margin-bottom: calc((var(--svelte-ui-select-height) - 32px) / 2);
294
- margin-right: calc(0px - var(--svelte-ui-select-icon-space));
295
300
  width: 32px;
296
- height: 32px;
301
+ padding-block: inherit;
297
302
  color: var(--svelte-ui-select-dropdown-icon-color);
298
303
  pointer-events: none;
299
304
  }
@@ -352,7 +357,6 @@
352
357
  * ============================================= */
353
358
  .multi-select.multi-select--inline .multi-select__trigger {
354
359
  padding: inherit;
355
- padding-right: var(--svelte-ui-select-icon-space-inline);
356
360
  background: transparent;
357
361
  border: none;
358
362
  border-radius: 0;
@@ -366,11 +370,12 @@
366
370
  min-height: 1lh;
367
371
  }
368
372
  .multi-select.multi-select--inline .multi-select__dropdown-icon {
369
- top: calc((1lh - 32px) / 2);
373
+ /* 複数行になりうるので、箱の中央ではなく最初の行にそろえる */
374
+ bottom: auto;
375
+ box-sizing: content-box;
376
+ height: 1lh;
377
+ padding-bottom: 0;
370
378
  right: 0;
371
- margin-top: 0;
372
- margin-bottom: 0;
373
- margin-right: calc(0px - var(--svelte-ui-select-icon-space-inline));
374
379
  }
375
380
 
376
381
  /* =============================================
@@ -168,6 +168,13 @@
168
168
  // =========================================================================
169
169
  // $derived
170
170
  // =========================================================================
171
+ // アイコンの領域は customStyle で消されないよう、customStyle より後ろに指定する
172
+ const triggerStyle = $derived(
173
+ [customStyle, `padding-right: var(--svelte-ui-select-icon-space${inline ? '-inline' : ''})`]
174
+ .filter(Boolean)
175
+ .join('; ')
176
+ );
177
+
171
178
  const fallbackId = `select-${Math.random().toString(36).substring(2, 15)}`;
172
179
  const baseId = $derived(id ?? fallbackId);
173
180
  const listboxId = $derived(`${baseId}-listbox`);
@@ -367,7 +374,7 @@
367
374
  aria-required={required ? 'true' : undefined}
368
375
  {tabindex}
369
376
  {disabled}
370
- style={customStyle}
377
+ style={triggerStyle}
371
378
  onfocus={handleFocus}
372
379
  onblur={handleBlur}
373
380
  onclick={handleTriggerClick}
@@ -398,9 +405,9 @@
398
405
  {:else if placeholder}
399
406
  <span class="select__placeholder">{placeholder}</span>
400
407
  {/if}
401
- <span class="select__dropdown-icon" aria-hidden="true">
402
- <Icon>arrow_drop_down</Icon>
403
- </span>
408
+ </span>
409
+ <span class="select__dropdown-icon" aria-hidden="true">
410
+ <Icon>arrow_drop_down</Icon>
404
411
  </span>
405
412
  </button>
406
413
 
@@ -483,10 +490,10 @@
483
490
  .select__trigger {
484
491
  display: flex;
485
492
  align-items: center;
493
+ position: relative;
486
494
  width: 100%;
487
495
  min-height: var(--svelte-ui-select-height);
488
496
  padding: var(--svelte-ui-select-padding);
489
- padding-right: var(--svelte-ui-select-icon-space);
490
497
  background: transparent;
491
498
  border: none;
492
499
  font-family: inherit;
@@ -499,7 +506,6 @@
499
506
  }
500
507
 
501
508
  .select__inner {
502
- position: relative;
503
509
  flex: 1;
504
510
  align-self: stretch;
505
511
  display: flex;
@@ -520,18 +526,17 @@
520
526
  color: var(--svelte-ui-select-placeholder-color);
521
527
  }
522
528
 
529
+ /* アイコンはテキストと同じ content box を基準に中央寄せする(padding-block: inherit)。
530
+ 絶対配置の基準はトリガーの padding box なので、そのままだと padding が上下非対称なときにズレる */
523
531
  .select__dropdown-icon {
524
532
  display: flex;
525
533
  justify-content: center;
526
534
  align-items: center;
527
535
  position: absolute;
528
- top: 0;
536
+ inset-block: 0;
529
537
  right: 4px;
530
- margin-top: calc((var(--svelte-ui-select-height) - 32px) / 2);
531
- margin-bottom: calc((var(--svelte-ui-select-height) - 32px) / 2);
532
- margin-right: calc(0px - var(--svelte-ui-select-icon-space));
533
538
  width: 32px;
534
- height: 32px;
539
+ padding-block: inherit;
535
540
  color: var(--svelte-ui-select-dropdown-icon-color);
536
541
  pointer-events: none;
537
542
  }
@@ -590,7 +595,6 @@
590
595
  * ============================================= */
591
596
  .select.select--inline .select__trigger {
592
597
  padding: inherit;
593
- padding-right: var(--svelte-ui-select-icon-space-inline);
594
598
  background: transparent;
595
599
  border: none;
596
600
  border-radius: 0;
@@ -604,11 +608,12 @@
604
608
  min-height: 1lh;
605
609
  }
606
610
  .select.select--inline .select__dropdown-icon {
607
- top: calc((1lh - 32px) / 2);
611
+ /* 複数行になりうるので、箱の中央ではなく最初の行にそろえる */
612
+ bottom: auto;
613
+ box-sizing: content-box;
614
+ height: 1lh;
615
+ padding-bottom: 0;
608
616
  right: 0;
609
- margin-top: 0;
610
- margin-bottom: 0;
611
- margin-right: calc(0px - var(--svelte-ui-select-icon-space-inline));
612
617
  }
613
618
 
614
619
  /* =============================================
@@ -12,6 +12,7 @@
12
12
  KeyboardHandler,
13
13
  BivariantValueHandler
14
14
  } from '../types/callbackHandlers';
15
+ import { getStyleFromNumber } from '../utils/style';
15
16
  import { getCurrentPath, matchPath } from '../utils/navPath';
16
17
  import { subscribeUrlChange } from '../utils/urlChange';
17
18
  import { t } from '../i18n';
@@ -52,6 +53,10 @@
52
53
  size?: ComponentSize;
53
54
  /** 現在/完了ステップの強調色。テーマ色より優先。 */
54
55
  color?: string;
56
+ /** 幅。数値は px として扱う。 */
57
+ width?: string | number | null;
58
+ minWidth?: string | number | null;
59
+ maxWidth?: string | number | null;
55
60
  /** 番号の代わりに各ステップの icon を表示。 @default false */
56
61
  showIcon?: boolean;
57
62
  /** 追加インラインスタイル。 */
@@ -103,6 +108,9 @@
103
108
  orientation = 'horizontal',
104
109
  size = 'medium',
105
110
  color,
111
+ width = null,
112
+ minWidth = null,
113
+ maxWidth = null,
106
114
  showIcon = false,
107
115
  customStyle = '',
108
116
 
@@ -204,12 +212,19 @@
204
212
  .join(' ')
205
213
  );
206
214
 
215
+ const widthStyle = $derived(getStyleFromNumber(width));
216
+ const minWidthStyle = $derived(getStyleFromNumber(minWidth));
217
+ const maxWidthStyle = $derived(getStyleFromNumber(maxWidth));
218
+
207
219
  const rootStyle = $derived(
208
220
  [
209
221
  // color 指定時は強調色を上書き。リングは半透明の色なので同率で合成した値も注入する。
210
222
  color ? `--internal-step-nav-accent:${color}` : '',
211
223
  color ? `--internal-step-nav-ring-color:color-mix(in srgb, ${color} 40%, transparent)` : '',
212
- customStyle
224
+ customStyle,
225
+ widthStyle ? `width: ${widthStyle}` : '',
226
+ minWidthStyle ? `min-width: ${minWidthStyle}` : '',
227
+ maxWidthStyle ? `max-width: ${maxWidthStyle}` : ''
213
228
  ]
214
229
  .filter(Boolean)
215
230
  .join('; ')
@@ -504,14 +519,12 @@ button.step-nav__step {
504
519
 
505
520
  /* upcoming: 淡いアウトライン + 番号 */
506
521
  .step-nav__step--upcoming .step-nav__marker {
507
- background: var(--svelte-ui-surface-color);
508
522
  color: var(--svelte-ui-step-nav-upcoming-text-color);
509
523
  border: var(--svelte-ui-step-nav-marker-border-width) solid var(--svelte-ui-step-nav-upcoming-marker-color);
510
524
  }
511
525
 
512
526
  /* in-progress: primary アウトライン + 番号 */
513
527
  .step-nav__step--in-progress .step-nav__marker {
514
- background: var(--svelte-ui-surface-color);
515
528
  color: var(--internal-step-nav-accent, var(--svelte-ui-step-nav-accent-color));
516
529
  border: var(--svelte-ui-step-nav-marker-border-width) solid var(--internal-step-nav-accent, var(--svelte-ui-step-nav-accent-color));
517
530
  }
@@ -525,14 +538,15 @@ button.step-nav__step {
525
538
 
526
539
  /* error: error 色 + error アイコン(進捗軸を上書き) */
527
540
  .step-nav__step--error .step-nav__marker {
528
- background: var(--svelte-ui-surface-color);
529
541
  color: var(--svelte-ui-step-nav-error-color);
530
542
  border: var(--svelte-ui-step-nav-marker-border-width) solid var(--svelte-ui-step-nav-error-color);
531
543
  }
532
544
 
533
- /* 表示中: 外周リング(フォーカス表示とは別レイヤー・別色) */
545
+ /* 表示中: 外周リング(フォーカス表示とは別レイヤー・別色)。
546
+ リングとマーカーの隙間に背景が透けるよう、box-shadow ではなく outline で描く */
534
547
  .step-nav__step--viewing .step-nav__marker {
535
- box-shadow: 0 0 0 var(--svelte-ui-step-nav-ring-offset) var(--svelte-ui-surface-color), 0 0 0 calc(var(--svelte-ui-step-nav-ring-offset) + var(--svelte-ui-step-nav-ring-width)) var(--internal-step-nav-ring-color, var(--svelte-ui-step-nav-ring-color));
548
+ outline: var(--svelte-ui-step-nav-ring-width) solid var(--internal-step-nav-ring-color, var(--svelte-ui-step-nav-ring-color));
549
+ outline-offset: var(--svelte-ui-step-nav-ring-offset);
536
550
  }
537
551
 
538
552
  /* ---- ラベル/説明 ---- */
@@ -31,6 +31,10 @@ export type StepNavProps = {
31
31
  size?: ComponentSize;
32
32
  /** 現在/完了ステップの強調色。テーマ色より優先。 */
33
33
  color?: string;
34
+ /** 幅。数値は px として扱う。 */
35
+ width?: string | number | null;
36
+ minWidth?: string | number | null;
37
+ maxWidth?: string | number | null;
34
38
  /** 番号の代わりに各ステップの icon を表示。 @default false */
35
39
  showIcon?: boolean;
36
40
  /** 追加インラインスタイル。 */
@@ -469,13 +469,18 @@
469
469
  const width = rect.width;
470
470
  if (!height || !width) return;
471
471
 
472
+ // 箱(ルート)は padding を持つため、textarea の実寸に padding 分を足す
473
+ const rootStyle = getComputedStyle(containerRef);
474
+ const paddingY = parseFloat(rootStyle.paddingTop) + parseFloat(rootStyle.paddingBottom);
475
+ const paddingX = parseFloat(rootStyle.paddingLeft) + parseFloat(rootStyle.paddingRight);
476
+
472
477
  // コンポーネント全体の高さは textarea に合わせる
473
- containerRef.style.height = `${height}px`;
478
+ containerRef.style.height = `${height + paddingY}px`;
474
479
 
475
480
  // 幅は、初期状態では width プロップを優先し、
476
481
  // ユーザーがリサイズ(ResizeObserver 経由)したあとは textarea の幅で上書きする
477
482
  if (forceWidth || !widthStyle) {
478
- containerRef.style.width = `${width}px`;
483
+ containerRef.style.width = `${width + paddingX}px`;
479
484
  }
480
485
 
481
486
  // display-text / link-text はコンテナにフィットさせる
@@ -549,6 +554,27 @@
549
554
  );
550
555
  const maxHeightStyle = $derived(getStyleFromNumber(maxHeight));
551
556
  const widthStyle = $derived(getStyleFromNumber(width));
557
+ // 箱(ルート)は border-box、内側のテキスト層は padding を持たないので縦 padding を差し引く
558
+ const innerMinHeightStyle = $derived(
559
+ `calc(${minHeightStyle} - var(--internal-textarea-vertical-padding))`
560
+ );
561
+ const innerMaxHeightStyle = $derived(
562
+ maxHeightStyle
563
+ ? `calc(${maxHeightStyle} - var(--internal-textarea-vertical-padding))`
564
+ : undefined
565
+ );
566
+ // アイコンの領域は customStyle で消されないよう、customStyle より後ろに指定する。
567
+ // style: ディレクティブは style 属性に勝つため、幅・最大高さも同じ文字列に混ぜる
568
+ const boxStyle = $derived(
569
+ [
570
+ customStyle,
571
+ clearable ? 'padding-right: var(--svelte-ui-textarea-icon-space)' : '',
572
+ widthStyle ? `width: ${widthStyle}` : '',
573
+ !inline && maxHeightStyle ? `max-height: ${maxHeightStyle}` : ''
574
+ ]
575
+ .filter(Boolean)
576
+ .join('; ')
577
+ );
552
578
  </script>
553
579
 
554
580
  <div
@@ -567,16 +593,14 @@
567
593
  class:textarea--readonly={readonly}
568
594
  class:textarea--focused={isFocused}
569
595
  data-testid="textarea"
570
- style:width={widthStyle}
571
- style:max-height={!inline ? maxHeightStyle : undefined}
596
+ style={boxStyle}
572
597
  >
573
598
  <div
574
599
  bind:this={displayTextRef}
575
600
  class="textarea__display-text"
576
601
  class:textarea__display-text--placeholder={!hasDisplayValue}
577
- style:min-height={minHeightStyle}
578
- style:max-height={maxHeightStyle}
579
- style={customStyle}
602
+ style:min-height={innerMinHeightStyle}
603
+ style:max-height={innerMaxHeightStyle}
580
604
  >
581
605
  {@html displayValue}
582
606
  </div>
@@ -599,8 +623,7 @@
599
623
  {spellcheck}
600
624
  {autocapitalize}
601
625
  class:resizable
602
- style:min-height={minHeightStyle}
603
- style={customStyle}
626
+ style:min-height={innerMinHeightStyle}
604
627
  onchange={handleChange}
605
628
  oninput={handleInput}
606
629
  onfocus={handleFocus}
@@ -634,9 +657,8 @@
634
657
  <div
635
658
  bind:this={linkTextRef}
636
659
  class="textarea__link-text"
637
- style:min-height={minHeightStyle}
638
- style:max-height={maxHeightStyle}
639
- style={customStyle}
660
+ style:min-height={innerMinHeightStyle}
661
+ style:max-height={innerMaxHeightStyle}
640
662
  >
641
663
  {@html linkHtmlValue}
642
664
  </div>
@@ -670,6 +692,13 @@
670
692
  width: auto;
671
693
  max-width: 100%;
672
694
  overflow: hidden;
695
+ --internal-textarea-vertical-padding: calc(
696
+ var(--svelte-ui-textarea-min-height) - var(--svelte-ui-textarea-line-height)
697
+ );
698
+ padding: var(--svelte-ui-textarea-padding);
699
+ background-color: var(--svelte-ui-textarea-bg);
700
+ box-shadow: 0 0 0 var(--svelte-ui-border-width) inset var(--svelte-ui-textarea-border-color);
701
+ border-radius: var(--svelte-ui-textarea-border-radius);
673
702
 
674
703
  &.textarea--full-height {
675
704
  height: 100%;
@@ -684,9 +713,8 @@
684
713
  height: 100%;
685
714
  padding: inherit;
686
715
  border: none;
687
- border-radius: var(--svelte-ui-textarea-border-radius);
688
- background-color: var(--svelte-ui-textarea-bg);
689
- box-shadow: 0 0 0 var(--svelte-ui-border-width) inset var(--svelte-ui-textarea-border-color);
716
+ border-radius: inherit;
717
+ background-color: transparent;
690
718
  font-size: inherit;
691
719
  font-weight: inherit;
692
720
  color: inherit;
@@ -700,11 +728,11 @@
700
728
  textarea {
701
729
  width: 100%;
702
730
  height: auto;
703
- min-height: var(--svelte-ui-textarea-min-height);
704
- padding: var(--svelte-ui-textarea-padding);
731
+ min-height: var(--svelte-ui-textarea-line-height);
732
+ padding: 0;
705
733
  background-color: transparent;
706
734
  border: none;
707
- border-radius: var(--svelte-ui-textarea-border-radius);
735
+ border-radius: inherit;
708
736
  font-family: inherit;
709
737
  font-size: inherit;
710
738
  font-weight: inherit;
@@ -724,10 +752,10 @@
724
752
  .textarea__link-text {
725
753
  display: block;
726
754
  width: 100%;
727
- min-height: var(--svelte-ui-textarea-min-height);
728
- padding: var(--svelte-ui-textarea-padding);
729
- background: inherit;
730
- border: inherit;
755
+ min-height: var(--svelte-ui-textarea-line-height);
756
+ padding: 0;
757
+ background: transparent;
758
+ border: none;
731
759
  font-size: inherit;
732
760
  font-weight: inherit;
733
761
  color: inherit;
@@ -774,6 +802,7 @@
774
802
  left: 0;
775
803
  width: 100%;
776
804
  height: 100%;
805
+ padding: inherit;
777
806
  pointer-events: none;
778
807
  z-index: 1;
779
808
  }
@@ -818,7 +847,11 @@
818
847
  /* =============================================
819
848
  * フォーカス効果バリエーション
820
849
  * ============================================= */
821
- .textarea--focus-outline textarea:focus {
850
+ textarea:focus {
851
+ outline: none;
852
+ }
853
+
854
+ .textarea--focus-outline.textarea--focused .textarea__wrapper {
822
855
  outline: var(--svelte-ui-focus-outline-inner);
823
856
  outline-offset: var(--svelte-ui-focus-outline-offset-inner);
824
857
  }
@@ -827,14 +860,6 @@
827
860
  background: var(--svelte-ui-hover-overlay);
828
861
  }
829
862
 
830
- .textarea--focus-background textarea:focus {
831
- outline: none;
832
- }
833
-
834
- .textarea--focus-none textarea:focus {
835
- outline: none;
836
- }
837
-
838
863
  /* =============================================
839
864
  * 状態管理(disabled, readonly等)
840
865
  * ============================================= */
@@ -853,7 +878,7 @@
853
878
  /* =============================================
854
879
  * 表示切り替え
855
880
  * ============================================= */
856
- /* 非フォーカス時: textarea要素自体を不可視化(枠線・背景はラッパーにあるため display-text のみ表示) */
881
+ /* 非フォーカス時: textarea要素自体を不可視化(枠線・背景はルートにあるため display-text のみ表示) */
857
882
  .textarea:not(.textarea--focused) textarea {
858
883
  opacity: 0;
859
884
  }
@@ -868,14 +893,6 @@
868
893
  * 機能バリエーション
869
894
  * ============================================= */
870
895
  /* clearable */
871
- .textarea--clearable {
872
- textarea,
873
- .textarea__display-text,
874
- .textarea__link-text {
875
- padding-right: var(--svelte-ui-textarea-icon-space);
876
- }
877
- }
878
-
879
896
  @media (hover: hover) {
880
897
  .textarea--clearable:hover .textarea__clear-button {
881
898
  opacity: 1;
@@ -913,29 +930,22 @@
913
930
  * デザインバリエーション
914
931
  * ============================================= */
915
932
  /* rounded */
916
- .textarea--rounded:not(.textarea--inline) .textarea__wrapper,
917
- .textarea--rounded:not(.textarea--inline) textarea {
933
+ .textarea--rounded:not(.textarea--inline) {
918
934
  border-radius: var(--svelte-ui-textarea-border-radius-rounded);
919
935
  }
920
936
 
921
937
  /* inline */
922
938
  .textarea--inline {
939
+ --internal-textarea-vertical-padding: 0px;
940
+ padding: 0;
941
+ background-color: transparent;
942
+ box-shadow: none;
943
+ border-radius: 0;
944
+
923
945
  .textarea__display-text,
924
946
  .textarea__link-text,
925
947
  textarea {
926
948
  min-height: auto;
927
- padding-top: inherit;
928
- padding-bottom: inherit;
929
- padding-left: inherit;
930
- padding-right: inherit;
931
- }
932
-
933
- .textarea__wrapper,
934
- textarea {
935
- padding: inherit;
936
- background: transparent;
937
- box-shadow: none;
938
- border-radius: 0;
939
949
  }
940
950
 
941
951
  textarea {
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.69",
5
+ "version": "0.0.71",
6
6
  "type": "module",
7
7
  "keywords": [
8
8
  "svelte",