@marianmeres/stuic 3.66.0 → 3.67.0

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 (75) hide show
  1. package/dist/actions/autoscroll.d.ts +7 -0
  2. package/dist/actions/autoscroll.js +7 -0
  3. package/dist/actions/focus-trap.d.ts +7 -0
  4. package/dist/actions/focus-trap.js +8 -3
  5. package/dist/actions/typeahead.svelte.js +40 -4
  6. package/dist/components/Carousel/Carousel.svelte +9 -2
  7. package/dist/components/Carousel/README.md +8 -2
  8. package/dist/components/Cart/Cart.svelte +3 -0
  9. package/dist/components/Cart/README.md +18 -1
  10. package/dist/components/Checkout/CheckoutOrderReview.svelte +4 -14
  11. package/dist/components/Checkout/README.md +184 -0
  12. package/dist/components/Checkout/_internal/checkout-utils.d.ts +6 -0
  13. package/dist/components/Checkout/_internal/checkout-utils.js +24 -0
  14. package/dist/components/Checkout/index.d.ts +1 -1
  15. package/dist/components/Checkout/index.js +1 -1
  16. package/dist/components/CommandMenu/CommandMenu.svelte +23 -7
  17. package/dist/components/CommandMenu/CommandMenu.svelte.d.ts +2 -0
  18. package/dist/components/CronInput/CronInput.svelte +44 -9
  19. package/dist/components/CronInput/CronInput.svelte.d.ts +2 -0
  20. package/dist/components/CronInput/README.md +145 -0
  21. package/dist/components/CronInput/cron-next-run.svelte.d.ts +11 -0
  22. package/dist/components/CronInput/cron-next-run.svelte.js +11 -0
  23. package/dist/components/CronInput/index.css +0 -8
  24. package/dist/components/DataTable/DataTable.svelte +99 -62
  25. package/dist/components/DataTable/DataTable.svelte.d.ts +13 -3
  26. package/dist/components/DataTable/README.md +79 -25
  27. package/dist/components/DataTable/index.css +7 -0
  28. package/dist/components/DropdownMenu/DropdownMenu.svelte +43 -26
  29. package/dist/components/DropdownMenu/DropdownMenu.svelte.d.ts +5 -1
  30. package/dist/components/DropdownMenu/README.md +37 -9
  31. package/dist/components/Input/FieldAssets.svelte +9 -7
  32. package/dist/components/Input/FieldAssets.svelte.d.ts +3 -7
  33. package/dist/components/Input/FieldFile.svelte +13 -7
  34. package/dist/components/Input/FieldFile.svelte.d.ts +4 -7
  35. package/dist/components/Input/FieldInput.svelte +10 -8
  36. package/dist/components/Input/FieldInput.svelte.d.ts +3 -8
  37. package/dist/components/Input/FieldInputLocalized.svelte +8 -7
  38. package/dist/components/Input/FieldInputLocalized.svelte.d.ts +2 -7
  39. package/dist/components/Input/FieldKeyValues.svelte +8 -7
  40. package/dist/components/Input/FieldKeyValues.svelte.d.ts +2 -7
  41. package/dist/components/Input/FieldLikeButton.svelte +9 -7
  42. package/dist/components/Input/FieldLikeButton.svelte.d.ts +3 -7
  43. package/dist/components/Input/FieldObject.svelte +8 -7
  44. package/dist/components/Input/FieldObject.svelte.d.ts +2 -7
  45. package/dist/components/Input/FieldOptions.svelte +9 -7
  46. package/dist/components/Input/FieldOptions.svelte.d.ts +3 -7
  47. package/dist/components/Input/FieldPhoneNumber.svelte +7 -8
  48. package/dist/components/Input/FieldPhoneNumber.svelte.d.ts +3 -8
  49. package/dist/components/Input/FieldSelect.svelte +9 -8
  50. package/dist/components/Input/FieldSelect.svelte.d.ts +3 -8
  51. package/dist/components/Input/FieldSwitch.svelte +9 -7
  52. package/dist/components/Input/FieldSwitch.svelte.d.ts +3 -7
  53. package/dist/components/Input/FieldTextarea.svelte +7 -8
  54. package/dist/components/Input/FieldTextarea.svelte.d.ts +3 -8
  55. package/dist/components/Input/README.md +20 -0
  56. package/dist/components/Input/_internal/InputWrap.svelte +2 -10
  57. package/dist/components/Input/_internal/InputWrap.svelte.d.ts +2 -10
  58. package/dist/components/Input/types.d.ts +28 -0
  59. package/dist/components/Nav/Nav.svelte +5 -4
  60. package/dist/components/Nav/Nav.svelte.d.ts +2 -2
  61. package/dist/components/Nav/README.md +2 -2
  62. package/dist/components/Nav/index.css +4 -0
  63. package/dist/components/Tree/README.md +189 -0
  64. package/dist/components/Tree/Tree.svelte +46 -2
  65. package/dist/components/Tree/Tree.svelte.d.ts +5 -0
  66. package/dist/utils/input-history.svelte.d.ts +12 -0
  67. package/dist/utils/input-history.svelte.js +12 -0
  68. package/dist/utils/observe-exists.svelte.d.ts +1 -0
  69. package/dist/utils/observe-exists.svelte.js +11 -3
  70. package/dist/utils/switch.svelte.d.ts +12 -0
  71. package/dist/utils/switch.svelte.js +12 -1
  72. package/docs/architecture.md +0 -1
  73. package/docs/testing.md +72 -0
  74. package/docs/upgrading.md +281 -0
  75. package/package.json +18 -19
@@ -1,10 +1,11 @@
1
1
  import type { Snippet } from "svelte";
2
2
  import type { ValidateOptions } from "../../actions/validate.svelte.js";
3
3
  import type { THC } from "../Thc/Thc.svelte";
4
+ import type { InputWrapClassProps } from "./types.js";
4
5
  type SnippetWithId = Snippet<[{
5
6
  id: string;
6
7
  }]>;
7
- export interface Props extends Record<string, any> {
8
+ export interface Props extends InputWrapClassProps, Record<string, any> {
8
9
  value: string;
9
10
  name: string;
10
11
  label?: SnippetWithId | THC;
@@ -21,12 +22,6 @@ export interface Props extends Record<string, any> {
21
22
  labelLeft?: boolean;
22
23
  labelLeftWidth?: "normal" | "wide";
23
24
  labelLeftBreakpoint?: number;
24
- classLabel?: string;
25
- classLabelBox?: string;
26
- classInputBox?: string;
27
- classInputBoxWrap?: string;
28
- classDescBox?: string;
29
- classBelowBox?: string;
30
25
  style?: string;
31
26
  onChange?: (value: string) => void;
32
27
  }
@@ -24,6 +24,7 @@
24
24
  import InputWrap from "./_internal/InputWrap.svelte";
25
25
  import FieldLikeButton from "./FieldLikeButton.svelte";
26
26
  import ListItemButton from "../ListItemButton/ListItemButton.svelte";
27
+ import type { InputWrapClassProps } from "./types.js";
27
28
 
28
29
  export interface Option {
29
30
  label: string;
@@ -32,7 +33,7 @@
32
33
 
33
34
  type SnippetWithId = Snippet<[{ id: string }]>;
34
35
 
35
- export interface Props extends Record<string, any> {
36
+ export interface Props extends InputWrapClassProps, Record<string, any> {
36
37
  trigger?: Snippet<[{ value: string; modal: ModalDialog }]>;
37
38
  modal?: ModalDialog;
38
39
  input?: HTMLInputElement;
@@ -53,13 +54,8 @@
53
54
  labelLeft?: boolean;
54
55
  labelLeftWidth?: "normal" | "wide";
55
56
  labelLeftBreakpoint?: number;
57
+ /** Classes for the inner FieldLikeButton element */
56
58
  classInput?: string;
57
- classLabel?: string;
58
- classLabelBox?: string;
59
- classInputBox?: string;
60
- classInputBoxWrap?: string;
61
- classDescBox?: string;
62
- classBelowBox?: string;
63
59
  classOption?: string;
64
60
  classOptionActive?: string;
65
61
  classOptgroup?: string;
@@ -154,8 +150,11 @@
154
150
  classLabelBox,
155
151
  classInputBox,
156
152
  classInputBoxWrap,
153
+ classInputBoxWrapInvalid,
157
154
  classDescBox,
155
+ classDescBoxToggle,
158
156
  classBelowBox,
157
+ classValidationBox,
159
158
  //
160
159
  classOption,
161
160
  classOptionActive,
@@ -524,8 +523,11 @@
524
523
  {classLabelBox}
525
524
  {classInputBox}
526
525
  {classInputBoxWrap}
526
+ {classInputBoxWrapInvalid}
527
527
  {classDescBox}
528
+ {classDescBoxToggle}
528
529
  {classBelowBox}
530
+ {classValidationBox}
529
531
  {style}
530
532
  validate={wrappedValidate}
531
533
  {required}
@@ -5,6 +5,7 @@ import type { TranslateFn } from "../../types.js";
5
5
  import { ModalDialog } from "../ModalDialog/index.js";
6
6
  import { NotificationsStack } from "../Notifications/index.js";
7
7
  import type { THC } from "../Thc/Thc.svelte";
8
+ import type { InputWrapClassProps } from "./types.js";
8
9
  export interface Option {
9
10
  label: string;
10
11
  value: any;
@@ -12,7 +13,7 @@ export interface Option {
12
13
  type SnippetWithId = Snippet<[{
13
14
  id: string;
14
15
  }]>;
15
- export interface Props extends Record<string, any> {
16
+ export interface Props extends InputWrapClassProps, Record<string, any> {
16
17
  trigger?: Snippet<[{
17
18
  value: string;
18
19
  modal: ModalDialog;
@@ -36,13 +37,8 @@ export interface Props extends Record<string, any> {
36
37
  labelLeft?: boolean;
37
38
  labelLeftWidth?: "normal" | "wide";
38
39
  labelLeftBreakpoint?: number;
40
+ /** Classes for the inner FieldLikeButton element */
39
41
  classInput?: string;
40
- classLabel?: string;
41
- classLabelBox?: string;
42
- classInputBox?: string;
43
- classInputBoxWrap?: string;
44
- classDescBox?: string;
45
- classBelowBox?: string;
46
42
  classOption?: string;
47
43
  classOptionActive?: string;
48
44
  classOptgroup?: string;
@@ -3,10 +3,11 @@
3
3
  import type { ValidateOptions } from "../../actions/validate.svelte.js";
4
4
  import type { TranslateFn } from "../../types.js";
5
5
  import type { THC } from "../Thc/Thc.svelte";
6
+ import type { InputWrapClassProps } from "./types.js";
6
7
 
7
8
  type SnippetWithId = Snippet<[{ id: string }]>;
8
9
 
9
- export interface Props extends Record<string, any> {
10
+ export interface Props extends InputWrapClassProps, Record<string, any> {
10
11
  input?: HTMLInputElement;
11
12
  /** Full phone number string, e.g. "+421905123456". Bindable. */
12
13
  value?: string;
@@ -46,14 +47,8 @@
46
47
  labelLeftWidth?: "normal" | "wide";
47
48
  labelLeftBreakpoint?: number;
48
49
  //
50
+ /** Classes for the tel <input> element */
49
51
  classInput?: string;
50
- classLabel?: string;
51
- classLabelBox?: string;
52
- classInputBox?: string;
53
- classInputBoxWrap?: string;
54
- classInputBoxWrapInvalid?: string;
55
- classDescBox?: string;
56
- classBelowBox?: string;
57
52
  classPrefixTrigger?: string;
58
53
  classPrefixDropdown?: string;
59
54
  style?: string;
@@ -121,7 +116,9 @@
121
116
  classInputBoxWrap,
122
117
  classInputBoxWrapInvalid,
123
118
  classDescBox,
119
+ classDescBoxToggle,
124
120
  classBelowBox,
121
+ classValidationBox,
125
122
  classPrefixTrigger,
126
123
  classPrefixDropdown,
127
124
  style,
@@ -283,7 +280,9 @@
283
280
  {classInputBoxWrap}
284
281
  {classInputBoxWrapInvalid}
285
282
  {classDescBox}
283
+ {classDescBoxToggle}
286
284
  {classBelowBox}
285
+ {classValidationBox}
287
286
  {validation}
288
287
  {style}
289
288
  >
@@ -2,10 +2,11 @@ import type { Snippet } from "svelte";
2
2
  import type { ValidateOptions } from "../../actions/validate.svelte.js";
3
3
  import type { TranslateFn } from "../../types.js";
4
4
  import type { THC } from "../Thc/Thc.svelte";
5
+ import type { InputWrapClassProps } from "./types.js";
5
6
  type SnippetWithId = Snippet<[{
6
7
  id: string;
7
8
  }]>;
8
- export interface Props extends Record<string, any> {
9
+ export interface Props extends InputWrapClassProps, Record<string, any> {
9
10
  input?: HTMLInputElement;
10
11
  /** Full phone number string, e.g. "+421905123456". Bindable. */
11
12
  value?: string;
@@ -43,14 +44,8 @@ export interface Props extends Record<string, any> {
43
44
  labelLeft?: boolean;
44
45
  labelLeftWidth?: "normal" | "wide";
45
46
  labelLeftBreakpoint?: number;
47
+ /** Classes for the tel <input> element */
46
48
  classInput?: string;
47
- classLabel?: string;
48
- classLabelBox?: string;
49
- classInputBox?: string;
50
- classInputBoxWrap?: string;
51
- classInputBoxWrapInvalid?: string;
52
- classDescBox?: string;
53
- classBelowBox?: string;
54
49
  classPrefixTrigger?: string;
55
50
  classPrefixDropdown?: string;
56
51
  style?: string;
@@ -3,11 +3,11 @@
3
3
  import type { HTMLSelectAttributes } from "svelte/elements";
4
4
  import type { ValidateOptions } from "../../actions/validate.svelte.js";
5
5
  import type { THC } from "../Thc/Thc.svelte";
6
- import type { FieldSelectOption } from "./types.js";
6
+ import type { FieldSelectOption, InputWrapClassProps } from "./types.js";
7
7
 
8
8
  type SnippetWithId = Snippet<[{ id: string }]>;
9
9
 
10
- export interface Props extends HTMLSelectAttributes {
10
+ export interface Props extends HTMLSelectAttributes, InputWrapClassProps {
11
11
  input?: HTMLSelectElement;
12
12
  value?: string | number;
13
13
  label?: SnippetWithId | THC;
@@ -28,13 +28,8 @@
28
28
  labelLeft?: boolean;
29
29
  labelLeftWidth?: "normal" | "wide";
30
30
  labelLeftBreakpoint?: number;
31
+ /** Classes for the <select> element */
31
32
  classInput?: string;
32
- classLabel?: string;
33
- classLabelBox?: string;
34
- classInputBox?: string;
35
- classInputBoxWrap?: string;
36
- classDescBox?: string;
37
- classBelowBox?: string;
38
33
  style?: string;
39
34
  }
40
35
  </script>
@@ -82,8 +77,11 @@
82
77
  classLabelBox,
83
78
  classInputBox,
84
79
  classInputBoxWrap,
80
+ classInputBoxWrapInvalid,
85
81
  classDescBox,
82
+ classDescBoxToggle,
86
83
  classBelowBox,
84
+ classValidationBox,
87
85
  style,
88
86
  //
89
87
  ...rest
@@ -128,8 +126,11 @@
128
126
  {classLabelBox}
129
127
  {classInputBox}
130
128
  {classInputBoxWrap}
129
+ {classInputBoxWrapInvalid}
131
130
  {classDescBox}
131
+ {classDescBoxToggle}
132
132
  {classBelowBox}
133
+ {classValidationBox}
133
134
  {validation}
134
135
  {style}
135
136
  >
@@ -2,11 +2,11 @@ import type { Snippet } from "svelte";
2
2
  import type { HTMLSelectAttributes } from "svelte/elements";
3
3
  import type { ValidateOptions } from "../../actions/validate.svelte.js";
4
4
  import type { THC } from "../Thc/Thc.svelte";
5
- import type { FieldSelectOption } from "./types.js";
5
+ import type { FieldSelectOption, InputWrapClassProps } from "./types.js";
6
6
  type SnippetWithId = Snippet<[{
7
7
  id: string;
8
8
  }]>;
9
- export interface Props extends HTMLSelectAttributes {
9
+ export interface Props extends HTMLSelectAttributes, InputWrapClassProps {
10
10
  input?: HTMLSelectElement;
11
11
  value?: string | number;
12
12
  label?: SnippetWithId | THC;
@@ -27,13 +27,8 @@ export interface Props extends HTMLSelectAttributes {
27
27
  labelLeft?: boolean;
28
28
  labelLeftWidth?: "normal" | "wide";
29
29
  labelLeftBreakpoint?: number;
30
+ /** Classes for the <select> element */
30
31
  classInput?: string;
31
- classLabel?: string;
32
- classLabelBox?: string;
33
- classInputBox?: string;
34
- classInputBoxWrap?: string;
35
- classDescBox?: string;
36
- classBelowBox?: string;
37
32
  style?: string;
38
33
  }
39
34
  declare const FieldSelect: import("svelte").Component<Props, {}, "value" | "input">;
@@ -2,10 +2,11 @@
2
2
  import type { Snippet } from "svelte";
3
3
  import type { ValidateOptions } from "../../actions/validate.svelte.js";
4
4
  import type { THC } from "../Thc/Thc.svelte";
5
+ import type { InputWrapClassProps } from "./types.js";
5
6
 
6
7
  type SnippetWithId = Snippet<[{ id: string }]>;
7
8
 
8
- export interface Props extends Record<string, any> {
9
+ export interface Props extends InputWrapClassProps, Record<string, any> {
9
10
  input?: HTMLInputElement;
10
11
  checked?: boolean;
11
12
  label?: SnippetWithId | THC;
@@ -27,13 +28,8 @@
27
28
  labelLeft?: boolean;
28
29
  labelLeftWidth?: "normal" | "wide";
29
30
  labelLeftBreakpoint?: number;
31
+ /** Classes for the underlying <Switch> element */
30
32
  classInput?: string;
31
- classLabel?: string;
32
- classLabelBox?: string;
33
- classInputBox?: string;
34
- classInputBoxWrap?: string;
35
- classDescBox?: string;
36
- classBelowBox?: string;
37
33
  style?: string;
38
34
  renderValue?: (rawValue: any) => string;
39
35
  }
@@ -79,8 +75,11 @@
79
75
  classLabelBox,
80
76
  classInputBox,
81
77
  classInputBoxWrap,
78
+ classInputBoxWrapInvalid,
82
79
  classDescBox,
80
+ classDescBoxToggle,
83
81
  classBelowBox,
82
+ classValidationBox,
84
83
  style = "",
85
84
  //
86
85
  renderValue,
@@ -111,8 +110,11 @@
111
110
  {classLabel}
112
111
  {classLabelBox}
113
112
  {classInputBox}
113
+ {classInputBoxWrapInvalid}
114
114
  {classDescBox}
115
+ {classDescBoxToggle}
115
116
  {classBelowBox}
117
+ {classValidationBox}
116
118
  {validation}
117
119
  classInputBoxWrap={twMerge("input-wrap-transparent", classInputBoxWrap)}
118
120
  {style}
@@ -1,10 +1,11 @@
1
1
  import type { Snippet } from "svelte";
2
2
  import type { ValidateOptions } from "../../actions/validate.svelte.js";
3
3
  import type { THC } from "../Thc/Thc.svelte";
4
+ import type { InputWrapClassProps } from "./types.js";
4
5
  type SnippetWithId = Snippet<[{
5
6
  id: string;
6
7
  }]>;
7
- export interface Props extends Record<string, any> {
8
+ export interface Props extends InputWrapClassProps, Record<string, any> {
8
9
  input?: HTMLInputElement;
9
10
  checked?: boolean;
10
11
  label?: SnippetWithId | THC;
@@ -26,13 +27,8 @@ export interface Props extends Record<string, any> {
26
27
  labelLeft?: boolean;
27
28
  labelLeftWidth?: "normal" | "wide";
28
29
  labelLeftBreakpoint?: number;
30
+ /** Classes for the underlying <Switch> element */
29
31
  classInput?: string;
30
- classLabel?: string;
31
- classLabelBox?: string;
32
- classInputBox?: string;
33
- classInputBoxWrap?: string;
34
- classDescBox?: string;
35
- classBelowBox?: string;
36
32
  style?: string;
37
33
  renderValue?: (rawValue: any) => string;
38
34
  }
@@ -3,10 +3,11 @@
3
3
  import type { HTMLTextareaAttributes } from "svelte/elements";
4
4
  import type { ValidateOptions } from "../../actions/validate.svelte.js";
5
5
  import type { THC } from "../Thc/Thc.svelte";
6
+ import type { InputWrapClassProps } from "./types.js";
6
7
 
7
8
  type SnippetWithId = Snippet<[{ id: string }]>;
8
9
 
9
- export interface Props extends HTMLTextareaAttributes {
10
+ export interface Props extends HTMLTextareaAttributes, InputWrapClassProps {
10
11
  input?: HTMLTextAreaElement;
11
12
  value?: string;
12
13
  label?: SnippetWithId | THC;
@@ -28,14 +29,8 @@
28
29
  labelLeft?: boolean;
29
30
  labelLeftWidth?: "normal" | "wide";
30
31
  labelLeftBreakpoint?: number;
32
+ /** Classes for the <textarea> element */
31
33
  classInput?: string;
32
- classLabel?: string;
33
- classLabelBox?: string;
34
- classInputBox?: string;
35
- classInputBoxWrap?: string;
36
- classInputBoxWrapInvalid?: string;
37
- classDescBox?: string;
38
- classBelowBox?: string;
39
34
  style?: string;
40
35
  }
41
36
  </script>
@@ -85,7 +80,9 @@
85
80
  classInputBoxWrap,
86
81
  classInputBoxWrapInvalid,
87
82
  classDescBox,
83
+ classDescBoxToggle,
88
84
  classBelowBox,
85
+ classValidationBox,
89
86
  style,
90
87
  //
91
88
  ...rest
@@ -117,7 +114,9 @@
117
114
  {classInputBoxWrap}
118
115
  {classInputBoxWrapInvalid}
119
116
  {classDescBox}
117
+ {classDescBoxToggle}
120
118
  {classBelowBox}
119
+ {classValidationBox}
121
120
  {validation}
122
121
  {style}
123
122
  >
@@ -2,10 +2,11 @@ import type { Snippet } from "svelte";
2
2
  import type { HTMLTextareaAttributes } from "svelte/elements";
3
3
  import type { ValidateOptions } from "../../actions/validate.svelte.js";
4
4
  import type { THC } from "../Thc/Thc.svelte";
5
+ import type { InputWrapClassProps } from "./types.js";
5
6
  type SnippetWithId = Snippet<[{
6
7
  id: string;
7
8
  }]>;
8
- export interface Props extends HTMLTextareaAttributes {
9
+ export interface Props extends HTMLTextareaAttributes, InputWrapClassProps {
9
10
  input?: HTMLTextAreaElement;
10
11
  value?: string;
11
12
  label?: SnippetWithId | THC;
@@ -30,14 +31,8 @@ export interface Props extends HTMLTextareaAttributes {
30
31
  labelLeft?: boolean;
31
32
  labelLeftWidth?: "normal" | "wide";
32
33
  labelLeftBreakpoint?: number;
34
+ /** Classes for the <textarea> element */
33
35
  classInput?: string;
34
- classLabel?: string;
35
- classLabelBox?: string;
36
- classInputBox?: string;
37
- classInputBoxWrap?: string;
38
- classInputBoxWrapInvalid?: string;
39
- classDescBox?: string;
40
- classBelowBox?: string;
41
36
  style?: string;
42
37
  }
43
38
  declare const FieldTextarea: import("svelte").Component<Props, {}, "value" | "input">;
@@ -48,6 +48,26 @@ A comprehensive form input system with multiple field components, validation sup
48
48
  | `inputBelow` | `Snippet \| THC` | Content below input |
49
49
  | `below` | `Snippet \| THC` | Content below entire field |
50
50
 
51
+ ## Shared wrapper class props (`InputWrapClassProps`)
52
+
53
+ Every `Field*` component that uses the shared label/input/description scaffolding accepts **the same 9 class props**, exported as the `InputWrapClassProps` interface from `@marianmeres/stuic`. Each Field extends that interface in its own `Props`, so the shape stays in sync and new wrapper targets are added in one place.
54
+
55
+ | Prop | Target |
56
+ | --------------------------- | ------------------------------------------------------------------ |
57
+ | `classLabel` | `<label>` element |
58
+ | `classLabelBox` | Wrapper around the label area |
59
+ | `classInputBox` | Wrapper around the whole input area |
60
+ | `classInputBoxWrap` | Inner input wrap (sibling to description/validation/below) |
61
+ | `classInputBoxWrapInvalid` | Added to `classInputBoxWrap` when validation fails |
62
+ | `classDescBox` | Description/help text box |
63
+ | `classDescBoxToggle` | Collapsible description's toggle button |
64
+ | `classBelowBox` | "Below" slot (rendered under the description) |
65
+ | `classValidationBox` | Validation message box |
66
+
67
+ Component-specific targets (e.g. `classInput` for the inner `<input>`/`<select>`/`<textarea>`, `classFileList` on `FieldFile`, `classOption`/`classOptgroup` on `FieldOptions`, `classPrefixTrigger` on `FieldPhoneNumber`, etc.) live on the component itself alongside these shared props.
68
+
69
+ > `FieldCheckbox` and `FieldRadios` have bespoke inline layouts and don't use the shared wrapper — they declare only the class props relevant to their own layout.
70
+
51
71
  ## Usage
52
72
 
53
73
  ### Basic Text Input
@@ -5,10 +5,11 @@
5
5
  import { twMerge } from "../../../utils/tw-merge.js";
6
6
  import { Collapsible } from "../../Collapsible/index.js";
7
7
  import Thc, { isTHCNotEmpty, type THC } from "../../Thc/Thc.svelte";
8
+ import type { InputWrapClassProps } from "../types.js";
8
9
 
9
10
  type SnippetWithId = Snippet<[{ id: string }]>;
10
11
 
11
- interface Props {
12
+ interface Props extends InputWrapClassProps {
12
13
  id: string;
13
14
  size?: "sm" | "md" | "lg" | string;
14
15
  class?: string;
@@ -28,15 +29,6 @@
28
29
  labelLeftWidth?: "normal" | "wide";
29
30
  labelLeftBreakpoint?: number;
30
31
  //
31
- classLabel?: string;
32
- classLabelBox?: string;
33
- classInputBox?: string;
34
- classInputBoxWrap?: string;
35
- classInputBoxWrapInvalid?: string;
36
- classDescBox?: string;
37
- classDescBoxToggle?: string;
38
- classBelowBox?: string;
39
- classValidationBox?: string;
40
32
  descriptionCollapsible?: boolean;
41
33
  descriptionDefaultExpanded?: boolean;
42
34
  style?: string;
@@ -1,10 +1,11 @@
1
1
  import type { Snippet } from "svelte";
2
2
  import type { ValidationResult } from "../../../actions/validate.svelte.js";
3
3
  import { type THC } from "../../Thc/Thc.svelte";
4
+ import type { InputWrapClassProps } from "../types.js";
4
5
  type SnippetWithId = Snippet<[{
5
6
  id: string;
6
7
  }]>;
7
- interface Props {
8
+ interface Props extends InputWrapClassProps {
8
9
  id: string;
9
10
  size?: "sm" | "md" | "lg" | string;
10
11
  class?: string;
@@ -22,15 +23,6 @@ interface Props {
22
23
  labelLeft?: boolean;
23
24
  labelLeftWidth?: "normal" | "wide";
24
25
  labelLeftBreakpoint?: number;
25
- classLabel?: string;
26
- classLabelBox?: string;
27
- classInputBox?: string;
28
- classInputBoxWrap?: string;
29
- classInputBoxWrapInvalid?: string;
30
- classDescBox?: string;
31
- classDescBoxToggle?: string;
32
- classBelowBox?: string;
33
- classValidationBox?: string;
34
26
  descriptionCollapsible?: boolean;
35
27
  descriptionDefaultExpanded?: boolean;
36
28
  style?: string;
@@ -9,3 +9,31 @@ export interface FieldRadiosOption {
9
9
  value?: string;
10
10
  description?: THC;
11
11
  }
12
+ /**
13
+ * Class props forwarded to the shared `InputWrap` scaffolding used by every
14
+ * `Field*` component that has a label/input/description layout.
15
+ *
16
+ * Field components extend this interface in their own `Props` so the class-prop
17
+ * shape stays consistent across the library. Component-specific class props
18
+ * (e.g. `classInput`, `classFileList`, `classOption`) live on the component itself.
19
+ */
20
+ export interface InputWrapClassProps {
21
+ /** Classes for the <label> element */
22
+ classLabel?: string;
23
+ /** Classes for the wrapper around the label area */
24
+ classLabelBox?: string;
25
+ /** Classes for the wrapper around the input area (contains input + inputBefore/After/Below) */
26
+ classInputBox?: string;
27
+ /** Classes for the inner input wrap (sibling to description/validation/below) */
28
+ classInputBoxWrap?: string;
29
+ /** Extra classes applied to `classInputBoxWrap` when the field is invalid */
30
+ classInputBoxWrapInvalid?: string;
31
+ /** Classes for the description box (the subtle hint below the input) */
32
+ classDescBox?: string;
33
+ /** Classes for the description's collapsible toggle button */
34
+ classDescBoxToggle?: string;
35
+ /** Classes for the "below" slot — rendered under the description */
36
+ classBelowBox?: string;
37
+ /** Classes for the validation message box */
38
+ classValidationBox?: string;
39
+ }
@@ -11,9 +11,9 @@
11
11
  id: string;
12
12
  /** Display label (supports localization) */
13
13
  label: MaybeLocalized;
14
- /** Navigation URL (use href OR onClick, not both) */
14
+ /** Navigation URL. If both `href` and `onClick` are provided, `onClick` runs first (useful for analytics) and the browser then follows the href. */
15
15
  href?: string;
16
- /** Click handler (alternative to href) */
16
+ /** Click handler. Alternative to `href`, or runs before navigation if both are set. */
17
17
  onClick?: () => void;
18
18
  /** Icon content (THC for flexibility: string, html, component) */
19
19
  icon?: THC;
@@ -603,7 +603,7 @@
603
603
  class={twMerge(!unstyled && NAV_CHILDREN_CLASSES, classChildren)}
604
604
  transition:slide={{ duration: transitionDuration }}
605
605
  >
606
- {#each item.children ?? [] as child}
606
+ {#each item.children ?? [] as child (child.id)}
607
607
  {@render renderItem(child, depth + 1)}
608
608
  {/each}
609
609
  </ul>
@@ -627,6 +627,7 @@
627
627
  data-expanding={!unstyled && isExpanding ? "" : undefined}
628
628
  data-disabled={!unstyled && item.disabled ? "" : undefined}
629
629
  data-touch-friendly={!unstyled && isTouchFriendly ? "" : undefined}
630
+ aria-current={active ? "page" : undefined}
630
631
  aria-disabled={item.disabled}
631
632
  tabindex={item.disabled ? -1 : 0}
632
633
  use:tooltip={() => ({
@@ -691,7 +692,7 @@
691
692
  </li>
692
693
  {/snippet}
693
694
 
694
- {#each group.items ?? [] as item}
695
+ {#each group.items ?? [] as item (item.id)}
695
696
  {@render renderItem(item, 0)}
696
697
  {/each}
697
698
  </ul>
@@ -9,9 +9,9 @@ export interface NavItem {
9
9
  id: string;
10
10
  /** Display label (supports localization) */
11
11
  label: MaybeLocalized;
12
- /** Navigation URL (use href OR onClick, not both) */
12
+ /** Navigation URL. If both `href` and `onClick` are provided, `onClick` runs first (useful for analytics) and the browser then follows the href. */
13
13
  href?: string;
14
- /** Click handler (alternative to href) */
14
+ /** Click handler. Alternative to `href`, or runs before navigation if both are set. */
15
15
  onClick?: () => void;
16
16
  /** Icon content (THC for flexibility: string, html, component) */
17
17
  icon?: THC;
@@ -48,8 +48,8 @@ interface NavGroup {
48
48
  items?: NavItem[];
49
49
  /** Group icon (optional) */
50
50
  icon?: THC;
51
- /** Whether the group starts collapsed */
52
- defaultCollapsed?: boolean;
51
+ /** Whether the group starts expanded (default: false — groups are collapsed by default) */
52
+ defaultExpanded?: boolean;
53
53
  /** Navigation URL for groups without items */
54
54
  href?: string;
55
55
  /** Click handler for groups without items */
@@ -66,6 +66,10 @@
66
66
  --stuic-nav-item-text-focus: var(--stuic-color-primary-foreground);
67
67
  }
68
68
 
69
+ :root.dark {
70
+ --stuic-nav-item-bg-hover: rgb(255 255 255 / 0.08);
71
+ }
72
+
69
73
  @layer components {
70
74
  /* =============================================================================
71
75
  BASE CONTAINER