@reliability-design/web 1.3.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -87,7 +87,7 @@ __export(index_exports, {
87
87
  RelTopbarUtilities: () => RelTopbarUtilities
88
88
  });
89
89
  module.exports = __toCommonJS(index_exports);
90
- var import_vars = require("./vars-I2PC74XD.css");
90
+ var import_vars = require("./vars-F54UGCHM.css");
91
91
 
92
92
  // src/components/rel-accordion/rel-accordion.ts
93
93
  var import_vars_v2 = require("./vars-v2-VMGWTFJZ.css");
@@ -522,7 +522,7 @@ RelAlert.styles = import_lit2.css`
522
522
 
523
523
  .rel-alert {
524
524
  display: flex;
525
- align-items: center;
525
+ align-items: flex-start;
526
526
  gap: var(--rel-spacing-space-xs);
527
527
  padding: var(--rel-spacing-space-sm) var(--rel-spacing-space-md);
528
528
  border-radius: var(--rel-borders-radius-border-radius-200);
@@ -548,9 +548,15 @@ RelAlert.styles = import_lit2.css`
548
548
  opacity: 0.9;
549
549
  }
550
550
 
551
+ .rel-icon {
552
+ margin-top: 2px;
553
+ flex-shrink: 0;
554
+ }
555
+
551
556
  .rel-icon svg {
552
557
  width: 16px;
553
558
  height: 16px;
559
+ display: block;
554
560
  }
555
561
 
556
562
  .rel-link {
@@ -563,6 +569,7 @@ RelAlert.styles = import_lit2.css`
563
569
 
564
570
  .rel-close {
565
571
  margin-left: auto;
572
+ align-self: flex-start;
566
573
  background: none;
567
574
  border: none;
568
575
  cursor: pointer;
@@ -912,46 +919,63 @@ RelBadge.styles = import_lit4.css`
912
919
  display: inline-flex;
913
920
  align-items: center;
914
921
  justify-content: center;
922
+ box-sizing: border-box;
915
923
  font-family: var(--rel-fontFamilies-primary, sans-serif);
916
924
  font-weight: var(--rel-fontWeights-semibold, 600);
917
925
  background-color: var(--rel-badge-bg, var(--rel-error-surface-default));
918
926
  color: var(--rel-badge-color, #ffffff);
919
- border-radius: 50%;
927
+ /* Large fixed radius (not 50%) — stays a perfect circle for 1-2 digits,
928
+ but becomes a pill instead of clipping when content like "999+"
929
+ is wider than the minimum circle. */
930
+ border-radius: 999px;
931
+ white-space: nowrap;
920
932
  }
921
933
 
922
- /* NUMBER BADGES */
934
+ /* NUMBER BADGES — min-width (not width) lets multi-character content
935
+ like "999+" grow the badge into a pill instead of overflowing a fixed circle. */
923
936
  .badge-sm {
924
- width: 20px;
937
+ min-width: 20px;
925
938
  height: 20px;
939
+ padding: 0 4px;
926
940
  font-size: var(--rel-fontSizes-xs, 12px);
927
941
  }
928
942
 
929
943
  .badge-md {
930
- width: 28px;
944
+ min-width: 28px;
931
945
  height: 28px;
946
+ padding: 0 6px;
932
947
  font-size: var(--rel-fontSizes-sm, 14px);
933
948
  }
934
949
 
935
950
  .badge-lg {
936
- width: 40px;
951
+ min-width: 40px;
937
952
  height: 40px;
953
+ padding: 0 8px;
938
954
  font-size: var(--rel-fontSizes-lg, 20px);
939
955
  }
940
956
 
941
- /* DOT BADGES */
957
+ /* DOT BADGES — explicitly reset min-width/padding inherited from the
958
+ number-badge size rules above; otherwise min-width wins over this
959
+ smaller width and the dot gets stretched into a squashed pill. */
942
960
  .badge-dot.badge-sm {
943
961
  width: 8px;
944
962
  height: 8px;
963
+ min-width: 0;
964
+ padding: 0;
945
965
  }
946
966
 
947
967
  .badge-dot.badge-md {
948
968
  width: 10px;
949
969
  height: 10px;
970
+ min-width: 0;
971
+ padding: 0;
950
972
  }
951
973
 
952
974
  .badge-dot.badge-lg {
953
975
  width: 12px;
954
976
  height: 12px;
977
+ min-width: 0;
978
+ padding: 0;
955
979
  }
956
980
  `;
957
981
  __decorateClass([
@@ -1092,6 +1116,8 @@ var RelButton = class extends import_lit6.LitElement {
1092
1116
  this.border = "corner-radius";
1093
1117
  this.disabled = false;
1094
1118
  this.ariaLabel = null;
1119
+ this.badge = "";
1120
+ this.badgeDot = false;
1095
1121
  this.handleHostClick = (e) => {
1096
1122
  if (this.disabled) {
1097
1123
  e.preventDefault();
@@ -1123,6 +1149,7 @@ var RelButton = class extends import_lit6.LitElement {
1123
1149
  }
1124
1150
  }
1125
1151
  render() {
1152
+ const showBadge = this.badgeDot || !!this.badge;
1126
1153
  return import_lit6.html`
1127
1154
  <button
1128
1155
  class="${this.classes}"
@@ -1133,12 +1160,36 @@ var RelButton = class extends import_lit6.LitElement {
1133
1160
  >
1134
1161
  <slot></slot>
1135
1162
  </button>
1163
+ ${showBadge ? import_lit6.html`<rel-badge
1164
+ class="btn-badge"
1165
+ variant=${this.badgeDot ? "dot" : "number"}
1166
+ number=${this.badge}
1167
+ size="sm"
1168
+ ></rel-badge>` : null}
1136
1169
  `;
1137
1170
  }
1138
1171
  };
1139
1172
  RelButton.styles = import_lit6.css`
1140
1173
  :host {
1141
1174
  display: inline-flex;
1175
+ position: relative;
1176
+ }
1177
+
1178
+ .btn-badge {
1179
+ position: absolute;
1180
+ top: -6px;
1181
+ right: -6px;
1182
+ pointer-events: none;
1183
+ }
1184
+
1185
+ /* Icon buttons are a 40px circle (radius 20). An 8px dot's center needs to
1186
+ land ON that circle's edge at the 45° corner point — which works out to
1187
+ roughly 2px inward from each edge (R - R/√2 - dot-radius ≈ 1.9px), not
1188
+ outside it like the numbered badge. Negative offsets here would float
1189
+ the dot in the empty bounding-box corner outside the visible circle. */
1190
+ .btn-badge[variant="dot"] {
1191
+ top: 2px;
1192
+ right: 2px;
1142
1193
  }
1143
1194
 
1144
1195
  button {
@@ -1379,6 +1430,12 @@ __decorateClass([
1379
1430
  __decorateClass([
1380
1431
  (0, import_decorators6.property)({ type: String, attribute: "aria-label" })
1381
1432
  ], RelButton.prototype, "ariaLabel", 2);
1433
+ __decorateClass([
1434
+ (0, import_decorators6.property)({ type: String })
1435
+ ], RelButton.prototype, "badge", 2);
1436
+ __decorateClass([
1437
+ (0, import_decorators6.property)({ type: Boolean, attribute: "badge-dot" })
1438
+ ], RelButton.prototype, "badgeDot", 2);
1382
1439
  RelButton = __decorateClass([
1383
1440
  (0, import_decorators6.customElement)("rel-button")
1384
1441
  ], RelButton);
@@ -2815,6 +2872,9 @@ var RelDatePicker = class extends import_lit12.LitElement {
2815
2872
  this.label = null;
2816
2873
  this.disabled = false;
2817
2874
  this.required = false;
2875
+ this.readonly = false;
2876
+ this.minDate = null;
2877
+ this.maxDate = null;
2818
2878
  this.min = null;
2819
2879
  this.max = null;
2820
2880
  this.weekStart = "sun";
@@ -2822,6 +2882,10 @@ var RelDatePicker = class extends import_lit12.LitElement {
2822
2882
  this.open = false;
2823
2883
  this.closeOnSelect = true;
2824
2884
  this.clearable = false;
2885
+ this.error = false;
2886
+ this.size = "medium";
2887
+ this.variant = "outlined";
2888
+ this.fullWidth = false;
2825
2889
  this.viewYear = (/* @__PURE__ */ new Date()).getFullYear();
2826
2890
  this.viewMonth = (/* @__PURE__ */ new Date()).getMonth();
2827
2891
  this.focusedDate = null;
@@ -2840,6 +2904,13 @@ var RelDatePicker = class extends import_lit12.LitElement {
2840
2904
  }
2841
2905
  };
2842
2906
  }
2907
+ // minDate/maxDate supersede the deprecated min/max aliases
2908
+ get minBound() {
2909
+ return this.minDate ?? this.min;
2910
+ }
2911
+ get maxBound() {
2912
+ return this.maxDate ?? this.max;
2913
+ }
2843
2914
  connectedCallback() {
2844
2915
  super.connectedCallback();
2845
2916
  if (this.value) {
@@ -2900,12 +2971,12 @@ var RelDatePicker = class extends import_lit12.LitElement {
2900
2971
  }
2901
2972
  isDateDisabledInternal(date) {
2902
2973
  if (this.disabled) return true;
2903
- if (this.min) {
2904
- const minDate = this.parseISODate(this.min);
2974
+ if (this.minBound) {
2975
+ const minDate = this.parseISODate(this.minBound);
2905
2976
  if (minDate && date < minDate) return true;
2906
2977
  }
2907
- if (this.max) {
2908
- const maxDate = this.parseISODate(this.max);
2978
+ if (this.maxBound) {
2979
+ const maxDate = this.parseISODate(this.maxBound);
2909
2980
  if (maxDate && date > maxDate) return true;
2910
2981
  }
2911
2982
  if (this.isDateDisabled && this.isDateDisabled(date)) {
@@ -2990,12 +3061,12 @@ var RelDatePicker = class extends import_lit12.LitElement {
2990
3061
  const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
2991
3062
  let startYear = currentYear - 100;
2992
3063
  let endYear = currentYear + 10;
2993
- if (this.min) {
2994
- const minDate = this.parseISODate(this.min);
3064
+ if (this.minBound) {
3065
+ const minDate = this.parseISODate(this.minBound);
2995
3066
  if (minDate) startYear = Math.max(startYear, minDate.getFullYear() - 5);
2996
3067
  }
2997
- if (this.max) {
2998
- const maxDate = this.parseISODate(this.max);
3068
+ if (this.maxBound) {
3069
+ const maxDate = this.parseISODate(this.maxBound);
2999
3070
  if (maxDate) endYear = Math.min(endYear, maxDate.getFullYear() + 5);
3000
3071
  }
3001
3072
  const years = [];
@@ -3005,7 +3076,7 @@ var RelDatePicker = class extends import_lit12.LitElement {
3005
3076
  return years.reverse();
3006
3077
  }
3007
3078
  toggleCalendarPopover() {
3008
- if (this.disabled) return;
3079
+ if (this.disabled || this.readonly) return;
3009
3080
  this.open = !this.open;
3010
3081
  this.dispatchEvent(
3011
3082
  new CustomEvent("rel-open-change", {
@@ -3045,7 +3116,7 @@ var RelDatePicker = class extends import_lit12.LitElement {
3045
3116
  }
3046
3117
  }
3047
3118
  selectDay(day) {
3048
- if (day.isDisabled || this.disabled) return;
3119
+ if (day.isDisabled || this.disabled || this.readonly) return;
3049
3120
  const newValue = this.formatISODate(day.date);
3050
3121
  this.value = newValue;
3051
3122
  this.dispatchEvent(
@@ -3179,12 +3250,25 @@ var RelDatePicker = class extends import_lit12.LitElement {
3179
3250
  const weekdayLabels = this.getWeekdayLabels();
3180
3251
  const months = this.getMonthsList();
3181
3252
  const years = this.getYearsList();
3253
+ const isError = this.error;
3254
+ const belowText = isError && this.errorMessage ? this.errorMessage : this.helperText;
3255
+ const describedById = belowText ? "date-picker-desc" : void 0;
3256
+ const wrapperClasses = [
3257
+ "date-picker-wrapper",
3258
+ this.size,
3259
+ isError ? "error" : "",
3260
+ this.readonly ? "readonly" : ""
3261
+ ].filter(Boolean).join(" ");
3182
3262
  return import_lit12.html`
3183
- <div class="date-picker-wrapper">
3184
- ${this.label ? import_lit12.html`<label class="label" id="date-picker-label">${this.label}</label>` : import_lit12.nothing}
3263
+ <div class="${wrapperClasses}">
3264
+ ${this.label ? import_lit12.html`<label class="label" id="date-picker-label"
3265
+ >${this.label}${this.required ? import_lit12.html`<span class="required-mark" aria-hidden="true">*</span>` : import_lit12.nothing}</label
3266
+ >` : import_lit12.nothing}
3267
+
3268
+ ${this.name ? import_lit12.html`<input type="hidden" name=${this.name} .value=${this.value ?? ""} />` : import_lit12.nothing}
3185
3269
 
3186
3270
  <button
3187
- class="trigger-button"
3271
+ class="trigger-button ${isError ? "error" : ""}"
3188
3272
  type="button"
3189
3273
  ?disabled=${this.disabled}
3190
3274
  @click=${this.toggleCalendarPopover}
@@ -3193,6 +3277,9 @@ var RelDatePicker = class extends import_lit12.LitElement {
3193
3277
  aria-haspopup="dialog"
3194
3278
  aria-expanded=${this.open}
3195
3279
  aria-required=${this.required}
3280
+ aria-invalid=${isError}
3281
+ aria-readonly=${this.readonly}
3282
+ aria-describedby=${describedById ?? import_lit12.nothing}
3196
3283
  aria-labelledby=${this.label ? "date-picker-label" : import_lit12.nothing}
3197
3284
  >
3198
3285
  <span class="trigger-content ${this.value ? "" : "placeholder"}">
@@ -3341,17 +3428,27 @@ var RelDatePicker = class extends import_lit12.LitElement {
3341
3428
  )}
3342
3429
  </div>
3343
3430
  </div>
3431
+
3432
+ ${belowText ? import_lit12.html`<div class="date-picker-support ${isError ? "error" : ""}" id=${describedById}>
3433
+ ${belowText}
3434
+ </div>` : import_lit12.nothing}
3344
3435
  </div>
3345
3436
  `;
3346
3437
  }
3347
3438
  };
3348
3439
  RelDatePicker.styles = import_lit12.css`
3349
3440
  :host {
3350
- display: inline-block;
3441
+ display: block;
3351
3442
  font-family: var(--rel-fontFamilies-primary);
3352
3443
  position: relative;
3353
3444
  }
3354
3445
 
3446
+ /* Intrinsic width by default; set fullWidth to fill the parent. */
3447
+ :host(:not([fullwidth])) {
3448
+ display: inline-block;
3449
+ width: auto;
3450
+ }
3451
+
3355
3452
  :host([disabled]) {
3356
3453
  opacity: 0.6;
3357
3454
  cursor: not-allowed;
@@ -3361,6 +3458,10 @@ RelDatePicker.styles = import_lit12.css`
3361
3458
  pointer-events: none;
3362
3459
  }
3363
3460
 
3461
+ :host([readonly]) .trigger-button {
3462
+ cursor: default;
3463
+ }
3464
+
3364
3465
  .date-picker-wrapper {
3365
3466
  display: flex;
3366
3467
  flex-direction: column;
@@ -3373,6 +3474,21 @@ RelDatePicker.styles = import_lit12.css`
3373
3474
  color: var(--rel-text-heading);
3374
3475
  }
3375
3476
 
3477
+ .required-mark {
3478
+ color: var(--rel-error-surface-default, #cc0000);
3479
+ margin-left: 2px;
3480
+ }
3481
+
3482
+ /* Support text (helperText / errorMessage) */
3483
+ .date-picker-support {
3484
+ font-size: var(--rel-fontSizes-xs);
3485
+ color: var(--rel-color-text-placeholder);
3486
+ }
3487
+
3488
+ .date-picker-support.error {
3489
+ color: var(--rel-error-surface-default, #cc0000);
3490
+ }
3491
+
3376
3492
  .trigger-button {
3377
3493
  width: 100%;
3378
3494
  min-width: 200px;
@@ -3407,6 +3523,28 @@ RelDatePicker.styles = import_lit12.css`
3407
3523
  background: var(--rel-surface-secondary);
3408
3524
  }
3409
3525
 
3526
+ /* Error state */
3527
+ .trigger-button.error,
3528
+ .trigger-button.error:hover:not(:disabled) {
3529
+ border-color: var(--rel-error-surface-default, #cc0000);
3530
+ }
3531
+
3532
+ .trigger-button.error:focus-visible {
3533
+ outline-color: var(--rel-error-surface-default, #cc0000);
3534
+ border-color: var(--rel-error-surface-default, #cc0000);
3535
+ }
3536
+
3537
+ /* Size variants (medium is the base .trigger-button style) */
3538
+ .date-picker-wrapper.small .trigger-button {
3539
+ padding: var(--rel-spacing-space-xs) var(--rel-spacing-space-sm);
3540
+ font-size: var(--rel-fontSizes-sm);
3541
+ }
3542
+
3543
+ .date-picker-wrapper.large .trigger-button {
3544
+ padding: var(--rel-spacing-space-md) var(--rel-spacing-space-md);
3545
+ font-size: var(--rel-fontSizes-lg);
3546
+ }
3547
+
3410
3548
  .trigger-content {
3411
3549
  flex: 1;
3412
3550
  color: var(--rel-text-primary);
@@ -3657,6 +3795,15 @@ __decorateClass([
3657
3795
  __decorateClass([
3658
3796
  (0, import_decorators12.property)({ type: Boolean })
3659
3797
  ], RelDatePicker.prototype, "required", 2);
3798
+ __decorateClass([
3799
+ (0, import_decorators12.property)({ type: Boolean, reflect: true })
3800
+ ], RelDatePicker.prototype, "readonly", 2);
3801
+ __decorateClass([
3802
+ (0, import_decorators12.property)({ type: String, attribute: "min-date" })
3803
+ ], RelDatePicker.prototype, "minDate", 2);
3804
+ __decorateClass([
3805
+ (0, import_decorators12.property)({ type: String, attribute: "max-date" })
3806
+ ], RelDatePicker.prototype, "maxDate", 2);
3660
3807
  __decorateClass([
3661
3808
  (0, import_decorators12.property)({ type: String })
3662
3809
  ], RelDatePicker.prototype, "min", 2);
@@ -3678,6 +3825,27 @@ __decorateClass([
3678
3825
  __decorateClass([
3679
3826
  (0, import_decorators12.property)({ type: Boolean })
3680
3827
  ], RelDatePicker.prototype, "clearable", 2);
3828
+ __decorateClass([
3829
+ (0, import_decorators12.property)({ type: Boolean })
3830
+ ], RelDatePicker.prototype, "error", 2);
3831
+ __decorateClass([
3832
+ (0, import_decorators12.property)({ type: String, attribute: "error-message" })
3833
+ ], RelDatePicker.prototype, "errorMessage", 2);
3834
+ __decorateClass([
3835
+ (0, import_decorators12.property)({ type: String, attribute: "helper-text" })
3836
+ ], RelDatePicker.prototype, "helperText", 2);
3837
+ __decorateClass([
3838
+ (0, import_decorators12.property)({ type: String })
3839
+ ], RelDatePicker.prototype, "name", 2);
3840
+ __decorateClass([
3841
+ (0, import_decorators12.property)({ type: String })
3842
+ ], RelDatePicker.prototype, "size", 2);
3843
+ __decorateClass([
3844
+ (0, import_decorators12.property)({ type: String, reflect: true })
3845
+ ], RelDatePicker.prototype, "variant", 2);
3846
+ __decorateClass([
3847
+ (0, import_decorators12.property)({ type: Boolean, reflect: true })
3848
+ ], RelDatePicker.prototype, "fullWidth", 2);
3681
3849
  __decorateClass([
3682
3850
  (0, import_decorators12.state)()
3683
3851
  ], RelDatePicker.prototype, "viewYear", 2);
@@ -13456,6 +13624,7 @@ var RelTabItem = class extends import_lit51.LitElement {
13456
13624
  this.label = "";
13457
13625
  this.value = "";
13458
13626
  this.badge = 0;
13627
+ this.badgeDot = false;
13459
13628
  this.disabled = false;
13460
13629
  }
13461
13630
  render() {
@@ -13471,6 +13640,9 @@ __decorateClass([
13471
13640
  __decorateClass([
13472
13641
  (0, import_decorators51.property)({ type: Number })
13473
13642
  ], RelTabItem.prototype, "badge", 2);
13643
+ __decorateClass([
13644
+ (0, import_decorators51.property)({ type: Boolean, attribute: "badge-dot" })
13645
+ ], RelTabItem.prototype, "badgeDot", 2);
13474
13646
  __decorateClass([
13475
13647
  (0, import_decorators51.property)({ type: Boolean })
13476
13648
  ], RelTabItem.prototype, "disabled", 2);
@@ -13493,6 +13665,7 @@ var RelTab = class extends import_lit51.LitElement {
13493
13665
  return this._tabItems.map((item) => ({
13494
13666
  label: item.label,
13495
13667
  badge: item.badge || 0,
13668
+ badgeDot: item.badgeDot,
13496
13669
  disabled: item.disabled
13497
13670
  }));
13498
13671
  }
@@ -13549,7 +13722,7 @@ var RelTab = class extends import_lit51.LitElement {
13549
13722
  @keydown="${(e) => this.onKeyDown(e, i)}"
13550
13723
  >
13551
13724
  <span class="tab-label">${tab.label}</span>
13552
- ${tab.badge ? import_lit51.html`<rel-badge number="${tab.badge}" variant="number" size="sm"></rel-badge>` : ""}
13725
+ ${tab.badgeDot ? import_lit51.html`<rel-badge variant="dot" size="sm"></rel-badge>` : tab.badge ? import_lit51.html`<rel-badge number="${tab.badge}" variant="number" size="sm"></rel-badge>` : ""}
13553
13726
  <span class="tab-underline"></span>
13554
13727
  </div>
13555
13728
  `
@@ -13660,10 +13833,31 @@ var RelTextfield = class extends import_lit52.LitElement {
13660
13833
  this.size = "medium";
13661
13834
  this.floatingLabel = false;
13662
13835
  this.variant = "default";
13836
+ this.disabled = false;
13837
+ this.required = false;
13838
+ this.multiline = false;
13839
+ this.rows = 3;
13840
+ this.error = false;
13841
+ this.readonly = false;
13842
+ this.fullWidth = true;
13663
13843
  this.internalState = "default";
13664
13844
  this._wasError = false;
13665
13845
  this.uniqueId = `textfield-${Math.random().toString(36).slice(2)}`;
13666
13846
  }
13847
+ get isDisabled() {
13848
+ return this.disabled || this.state === "disabled";
13849
+ }
13850
+ get isError() {
13851
+ return this.error || this.state === "error";
13852
+ }
13853
+ // prefixIcon supersedes the deprecated leftIcon
13854
+ get leadingIcon() {
13855
+ return this.prefixIcon ?? this.leftIcon;
13856
+ }
13857
+ // helperText supersedes the deprecated supportingText
13858
+ get helper() {
13859
+ return this.helperText ?? this.supportingText;
13860
+ }
13667
13861
  connectedCallback() {
13668
13862
  super.connectedCallback();
13669
13863
  const leftIcon = this.getAttribute("lefticon") || this.getAttribute("leftIcon");
@@ -13672,7 +13866,7 @@ var RelTextfield = class extends import_lit52.LitElement {
13672
13866
  if (rightIcon) this.rightIcon = rightIcon;
13673
13867
  }
13674
13868
  handleFocus() {
13675
- if (this.state !== "disabled") {
13869
+ if (!this.isDisabled) {
13676
13870
  this.internalState = "focused";
13677
13871
  }
13678
13872
  }
@@ -13700,51 +13894,108 @@ var RelTextfield = class extends import_lit52.LitElement {
13700
13894
  );
13701
13895
  }
13702
13896
  render() {
13897
+ const isError = this.isError;
13898
+ const stateClass = this.isDisabled ? "disabled" : isError ? "error" : "default";
13899
+ const leadingIcon = this.leadingIcon;
13900
+ const inputId = this.id || this.uniqueId;
13901
+ const legendId = `${inputId}-legend`;
13902
+ const belowText = isError && this.errorMessage ? this.errorMessage : this.helper;
13903
+ const describedById = belowText ? `${inputId}-desc` : void 0;
13703
13904
  const wrapperClasses = [
13704
13905
  "textfield-wrapper",
13705
13906
  this.size,
13706
- this.state,
13907
+ stateClass,
13908
+ this.readonly ? "readonly" : "",
13707
13909
  this.internalState === "focused" ? "focused" : "",
13708
13910
  this.value ? "has-value" : ""
13709
13911
  ].filter(Boolean).join(" ");
13710
13912
  const inputClasses = [
13711
13913
  "textfield-input",
13712
- this.leftIcon ? "has-left-icon" : "",
13713
- this.rightIcon ? "has-right-icon" : ""
13914
+ !this.multiline && leadingIcon ? "has-left-icon" : "",
13915
+ !this.multiline && this.rightIcon && this.variant !== "legend" ? "has-right-icon" : ""
13714
13916
  ].filter(Boolean).join(" ");
13917
+ const labelTemplate = (extraClass = "") => {
13918
+ if (!this.label) return "";
13919
+ return import_lit52.html`<label
13920
+ class="textfield-label${extraClass}"
13921
+ for="${inputId}"
13922
+ >${this.label}${this.required ? import_lit52.html`<span class="required-mark" aria-hidden="true">*</span>` : ""}</label>`;
13923
+ };
13924
+ const ariaLabelledby = this.variant === "legend" && this.label ? legendId : void 0;
13925
+ const inputTemplate = this.multiline ? import_lit52.html`<textarea
13926
+ id="${inputId}"
13927
+ class="${inputClasses}"
13928
+ placeholder="${this.placeholder}"
13929
+ .value="${this.value}"
13930
+ rows="${this.rows}"
13931
+ name="${this.name ?? import_lit52.nothing}"
13932
+ maxlength="${this.maxLength ?? import_lit52.nothing}"
13933
+ ?disabled="${this.isDisabled}"
13934
+ ?readonly="${this.readonly}"
13935
+ ?required="${this.required}"
13936
+ aria-invalid="${isError}"
13937
+ aria-required="${this.required}"
13938
+ aria-labelledby="${ariaLabelledby ?? import_lit52.nothing}"
13939
+ aria-describedby="${describedById ?? import_lit52.nothing}"
13940
+ @focus="${this.handleFocus}"
13941
+ @blur="${this.handleBlur}"
13942
+ @input="${this.handleInput}"
13943
+ ></textarea>` : import_lit52.html`<input
13944
+ type="${this.type}"
13945
+ id="${inputId}"
13946
+ class="${inputClasses}"
13947
+ placeholder="${this.placeholder}"
13948
+ .value="${this.value}"
13949
+ name="${this.name ?? import_lit52.nothing}"
13950
+ maxlength="${this.maxLength ?? import_lit52.nothing}"
13951
+ ?disabled="${this.isDisabled}"
13952
+ ?readonly="${this.readonly}"
13953
+ ?required="${this.required}"
13954
+ aria-invalid="${isError}"
13955
+ aria-required="${this.required}"
13956
+ aria-labelledby="${ariaLabelledby ?? import_lit52.nothing}"
13957
+ aria-describedby="${describedById ?? import_lit52.nothing}"
13958
+ @focus="${this.handleFocus}"
13959
+ @blur="${this.handleBlur}"
13960
+ @input="${this.handleInput}"
13961
+ />`;
13962
+ const leadingIconTemplate = leadingIcon ? import_lit52.html`<rel-icon
13963
+ name="${leadingIcon}"
13964
+ size="sm"
13965
+ class="icon left"
13966
+ role="${this.prefixIconAriaLabel ? "img" : import_lit52.nothing}"
13967
+ aria-label="${this.prefixIconAriaLabel ?? import_lit52.nothing}"
13968
+ aria-hidden="${this.prefixIconAriaLabel ? import_lit52.nothing : "true"}"
13969
+ ></rel-icon>` : "";
13970
+ const belowTemplate = belowText ? import_lit52.html`<div class="textfield-support ${isError ? "error" : ""}" id="${describedById}">
13971
+ ${belowText}
13972
+ </div>` : "";
13973
+ if (this.variant === "legend") {
13974
+ const fieldsetClass = ["field-fieldset", this.multiline ? "multiline" : ""].filter(Boolean).join(" ");
13975
+ return import_lit52.html`
13976
+ <div class="${wrapperClasses}">
13977
+ <fieldset class="${fieldsetClass}"><legend class="field-legend" id="${legendId}">${this.label ?? ""}${this.required ? import_lit52.html`<span class="required-mark" aria-hidden="true">*</span>` : ""}</legend>
13978
+ ${!this.multiline ? leadingIconTemplate : ""}
13979
+ ${inputTemplate}
13980
+ </fieldset>
13981
+ ${belowTemplate}
13982
+ </div>
13983
+ `;
13984
+ }
13715
13985
  return import_lit52.html`
13716
13986
  <div class="${wrapperClasses}">
13717
- ${!this.floatingLabel && this.label ? import_lit52.html`<label class="textfield-label" for="${this.uniqueId}">${this.label}</label>` : ""}
13987
+ ${!this.floatingLabel ? labelTemplate() : ""}
13718
13988
 
13719
- <div
13720
- class="textfield-container ${this.state === "disabled" ? "disabled" : ""}"
13721
- >
13722
- ${this.floatingLabel && this.label ? import_lit52.html`<label
13723
- class="textfield-label${this.leftIcon ? " has-left-icon" : ""}"
13724
- for="${this.uniqueId}">${this.label}</label>` : ""}
13725
- ${this.leftIcon ? import_lit52.html`<rel-icon name="${this.leftIcon}" size="sm" class="icon left"></rel-icon>` : ""}
13989
+ <div class="${["textfield-container", this.isDisabled ? "disabled" : "", this.multiline ? "multiline" : ""].filter(Boolean).join(" ")}">
13990
+ ${this.floatingLabel ? labelTemplate(leadingIcon && !this.multiline ? " has-left-icon" : "") : ""}
13991
+ ${!this.multiline ? leadingIconTemplate : ""}
13726
13992
 
13727
- <input
13728
- type="${this.type}"
13729
- id="${this.uniqueId}"
13730
- class="${inputClasses}"
13731
- placeholder="${this.placeholder}"
13732
- .value="${this.value}"
13733
- ?disabled="${this.state === "disabled"}"
13734
- aria-invalid="${this.state === "error"}"
13735
- @focus="${this.handleFocus}"
13736
- @blur="${this.handleBlur}"
13737
- @input="${this.handleInput}"
13738
- />
13993
+ ${inputTemplate}
13739
13994
 
13740
- ${this.rightIcon ? import_lit52.html`<rel-icon name="${this.rightIcon}" size="sm" class="icon right"></rel-icon>` : ""}
13995
+ ${!this.multiline && this.rightIcon ? import_lit52.html`<rel-icon name="${this.rightIcon}" size="sm" class="icon right"></rel-icon>` : ""}
13741
13996
  </div>
13742
13997
 
13743
- ${this.supportingText ? import_lit52.html`<div
13744
- class="textfield-support ${this.state === "error" ? "error" : ""}"
13745
- >
13746
- ${this.supportingText}
13747
- </div>` : ""}
13998
+ ${belowTemplate}
13748
13999
  </div>
13749
14000
  `;
13750
14001
  }
@@ -13770,6 +14021,11 @@ RelTextfield.styles = import_lit52.css`
13770
14021
  color: var(--rel-color-text-heading);
13771
14022
  }
13772
14023
 
14024
+ .required-mark {
14025
+ color: var(--rel-error-surface-default, #CC0000);
14026
+ margin-left: 2px;
14027
+ }
14028
+
13773
14029
  /* Floating label variant */
13774
14030
  :host([floatinglabel]) .textfield-wrapper {
13775
14031
  gap: 0;
@@ -13798,6 +14054,12 @@ RelTextfield.styles = import_lit52.css`
13798
14054
  white-space: nowrap;
13799
14055
  }
13800
14056
 
14057
+ /* Floating label in multiline mode anchors to top instead of vertical center */
14058
+ :host([floatinglabel][multiline]) .textfield-label {
14059
+ top: var(--rel-spacing-space-sm);
14060
+ transform: none;
14061
+ }
14062
+
13801
14063
  :host([floatinglabel]) .textfield-wrapper.focused .textfield-label,
13802
14064
  :host([floatinglabel]) .textfield-wrapper.has-value .textfield-label {
13803
14065
  top: 0;
@@ -13861,6 +14123,11 @@ RelTextfield.styles = import_lit52.css`
13861
14123
  cursor: not-allowed;
13862
14124
  }
13863
14125
 
14126
+ .textfield-container.multiline {
14127
+ overflow: visible;
14128
+ align-items: flex-start;
14129
+ }
14130
+
13864
14131
  /* Input */
13865
14132
  .textfield-input {
13866
14133
  flex: 1;
@@ -13878,6 +14145,25 @@ RelTextfield.styles = import_lit52.css`
13878
14145
  color: var(--rel-color-text-disabled);
13879
14146
  }
13880
14147
 
14148
+ /* Textarea */
14149
+ textarea.textfield-input {
14150
+ resize: vertical;
14151
+ min-height: 80px;
14152
+ width: 100%;
14153
+ box-sizing: border-box;
14154
+ }
14155
+
14156
+ textarea.textfield-input::-webkit-scrollbar {
14157
+ width: 4px;
14158
+ }
14159
+ textarea.textfield-input::-webkit-scrollbar-track {
14160
+ background: transparent;
14161
+ }
14162
+ textarea.textfield-input::-webkit-scrollbar-thumb {
14163
+ background: var(--rel-color-text-placeholder);
14164
+ border-radius: 2px;
14165
+ }
14166
+
13881
14167
  .icon {
13882
14168
  position: absolute;
13883
14169
  display: flex;
@@ -13932,7 +14218,8 @@ RelTextfield.styles = import_lit52.css`
13932
14218
  box-shadow: 0 0 0 1px var(--rel-color-text-primary);
13933
14219
  }
13934
14220
 
13935
- .textfield-wrapper.error .textfield-container {
14221
+ .textfield-wrapper.error .textfield-container,
14222
+ .textfield-wrapper.error .textfield-container:focus-within {
13936
14223
  border-color: var(--rel-error-surface-default);
13937
14224
  box-shadow: none;
13938
14225
  }
@@ -13941,6 +14228,10 @@ RelTextfield.styles = import_lit52.css`
13941
14228
  border-color: var(--rel-color-border-default);
13942
14229
  }
13943
14230
 
14231
+ .textfield-wrapper.disabled .textfield-label {
14232
+ color: var(--rel-color-text-disabled);
14233
+ }
14234
+
13944
14235
  /* Supporting text */
13945
14236
  .textfield-support {
13946
14237
  font-size: var(--rel-fontSizes-xs);
@@ -14009,6 +14300,104 @@ RelTextfield.styles = import_lit52.css`
14009
14300
  background: var(--rel-color-bg-tertiary);
14010
14301
  border-bottom-color: var(--rel-color-border-default);
14011
14302
  }
14303
+
14304
+ /* ── Full-width control (fullWidth defaults true → block, fills parent) ── */
14305
+ :host(:not([fullwidth])) {
14306
+ display: inline-block;
14307
+ width: auto;
14308
+ }
14309
+ :host(:not([fullwidth])) .textfield-wrapper {
14310
+ width: auto;
14311
+ }
14312
+
14313
+ /* ── Legend (notched-outline) variant ──
14314
+ Native <fieldset>/<legend>: the legend notches the top border and stays
14315
+ fixed in place across all states (no float/animation). */
14316
+ :host([variant="legend"]) .field-fieldset {
14317
+ position: relative;
14318
+ margin: 0;
14319
+ min-inline-size: 0;
14320
+ padding: 0;
14321
+ border: 1px solid var(--rel-color-border-default);
14322
+ border-radius: var(--rel-borders-radius-border-radius-100);
14323
+ background: var(--rel-color-bg-primary);
14324
+ transition: border-color 0.2s;
14325
+ }
14326
+
14327
+ :host([variant="legend"]) .field-legend {
14328
+ margin-left: var(--rel-spacing-space-sm);
14329
+ padding: 0 var(--rel-spacing-space-2xs);
14330
+ font-size: var(--rel-fontSizes-sm);
14331
+ font-weight: var(--rel-fontWeights-medium);
14332
+ line-height: 1;
14333
+ color: var(--rel-color-text-heading);
14334
+ }
14335
+
14336
+ :host([variant="legend"]) .textfield-input {
14337
+ width: 100%;
14338
+ box-sizing: border-box;
14339
+ }
14340
+
14341
+ /* Prefix icon (legend): vertically centered; input inset via has-left-icon */
14342
+ :host([variant="legend"]) .icon.left {
14343
+ top: 50%;
14344
+ left: var(--rel-spacing-space-sm);
14345
+ transform: translateY(-50%);
14346
+ }
14347
+ :host([variant="legend"]) .field-fieldset.multiline .icon.left {
14348
+ top: var(--rel-spacing-space-sm);
14349
+ transform: none;
14350
+ }
14351
+
14352
+ /* Hover */
14353
+ :host([variant="legend"]) .field-fieldset:hover {
14354
+ border-color: var(--rel-color-text-placeholder);
14355
+ }
14356
+
14357
+ /* Focus — border-color only. A box-shadow/outline ring hugs the full
14358
+ rectangular border-box and would draw across the notch, appearing to
14359
+ strike through the legend. The legend also recolors on focus (below). */
14360
+ :host([variant="legend"]) .textfield-wrapper.focused .field-fieldset,
14361
+ :host([variant="legend"]) .field-fieldset:focus-within {
14362
+ border-color: var(--rel-color-border-focus);
14363
+ box-shadow: none;
14364
+ }
14365
+ :host([variant="legend"]) .textfield-wrapper.focused .field-legend,
14366
+ :host([variant="legend"]) .field-fieldset:focus-within .field-legend {
14367
+ color: var(--rel-color-border-focus);
14368
+ }
14369
+
14370
+ /* Error */
14371
+ :host([variant="legend"]) .textfield-wrapper.error .field-fieldset {
14372
+ border-color: var(--rel-error-surface-default);
14373
+ box-shadow: none;
14374
+ }
14375
+ :host([variant="legend"]) .textfield-wrapper.error .field-legend {
14376
+ color: var(--rel-error-surface-default);
14377
+ }
14378
+
14379
+ /* Disabled */
14380
+ :host([variant="legend"]) .textfield-wrapper.disabled .field-fieldset {
14381
+ background: var(--rel-color-bg-tertiary);
14382
+ border-color: var(--rel-color-border-default);
14383
+ cursor: not-allowed;
14384
+ }
14385
+ :host([variant="legend"]) .textfield-wrapper.disabled .field-legend {
14386
+ color: var(--rel-color-text-disabled);
14387
+ }
14388
+
14389
+ /* Readonly */
14390
+ :host([variant="legend"]) .textfield-wrapper.readonly .field-fieldset {
14391
+ cursor: default;
14392
+ }
14393
+
14394
+ /* Legend font tracks size */
14395
+ :host([variant="legend"]) .textfield-wrapper.small .field-legend {
14396
+ font-size: var(--rel-fontSizes-xs);
14397
+ }
14398
+ :host([variant="legend"]) .textfield-wrapper.large .field-legend {
14399
+ font-size: var(--rel-fontSizes-md);
14400
+ }
14012
14401
  `;
14013
14402
  __decorateClass([
14014
14403
  (0, import_decorators52.property)({ type: String })
@@ -14043,6 +14432,45 @@ __decorateClass([
14043
14432
  __decorateClass([
14044
14433
  (0, import_decorators52.property)({ type: String, reflect: true })
14045
14434
  ], RelTextfield.prototype, "variant", 2);
14435
+ __decorateClass([
14436
+ (0, import_decorators52.property)({ type: Boolean })
14437
+ ], RelTextfield.prototype, "disabled", 2);
14438
+ __decorateClass([
14439
+ (0, import_decorators52.property)({ type: Boolean })
14440
+ ], RelTextfield.prototype, "required", 2);
14441
+ __decorateClass([
14442
+ (0, import_decorators52.property)({ type: Boolean, reflect: true })
14443
+ ], RelTextfield.prototype, "multiline", 2);
14444
+ __decorateClass([
14445
+ (0, import_decorators52.property)({ type: Number })
14446
+ ], RelTextfield.prototype, "rows", 2);
14447
+ __decorateClass([
14448
+ (0, import_decorators52.property)({ type: String })
14449
+ ], RelTextfield.prototype, "prefixIcon", 2);
14450
+ __decorateClass([
14451
+ (0, import_decorators52.property)({ type: String })
14452
+ ], RelTextfield.prototype, "prefixIconAriaLabel", 2);
14453
+ __decorateClass([
14454
+ (0, import_decorators52.property)({ type: Boolean })
14455
+ ], RelTextfield.prototype, "error", 2);
14456
+ __decorateClass([
14457
+ (0, import_decorators52.property)({ type: String })
14458
+ ], RelTextfield.prototype, "errorMessage", 2);
14459
+ __decorateClass([
14460
+ (0, import_decorators52.property)({ type: String })
14461
+ ], RelTextfield.prototype, "helperText", 2);
14462
+ __decorateClass([
14463
+ (0, import_decorators52.property)({ type: Boolean })
14464
+ ], RelTextfield.prototype, "readonly", 2);
14465
+ __decorateClass([
14466
+ (0, import_decorators52.property)({ type: Number })
14467
+ ], RelTextfield.prototype, "maxLength", 2);
14468
+ __decorateClass([
14469
+ (0, import_decorators52.property)({ type: String })
14470
+ ], RelTextfield.prototype, "name", 2);
14471
+ __decorateClass([
14472
+ (0, import_decorators52.property)({ type: Boolean, reflect: true })
14473
+ ], RelTextfield.prototype, "fullWidth", 2);
14046
14474
  __decorateClass([
14047
14475
  (0, import_decorators52.state)()
14048
14476
  ], RelTextfield.prototype, "internalState", 2);