@studiocms/ui 0.4.14 → 0.4.15

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.
@@ -6,91 +6,103 @@ import Input from '../Input/Input.astro';
6
6
  import './searchselect.css';
7
7
 
8
8
  /**
9
- * An option in the select dropdown
9
+ * Represents an option in the select component.
10
10
  */
11
- interface Option {
11
+ export interface SearchSelectOption {
12
12
  /**
13
- * The label of the option.
13
+ * The label of the option displayed to the user.
14
14
  */
15
15
  label: string;
16
16
  /**
17
- * The value of the option.
17
+ * The value of the option used for form submission.
18
18
  */
19
19
  value: string;
20
20
  /**
21
- * Whether the option is disabled.
21
+ * Whether the option is disabled. Defaults to `false`.
22
22
  */
23
23
  disabled?: boolean;
24
24
  }
25
25
 
26
26
  /**
27
- * The props for the search select component.
27
+ * Base properties shared by both single and multiple select components.
28
28
  */
29
- type NonMultipleProps = {
29
+ export interface SearchSelectBaseProps {
30
30
  /**
31
- * Whether the select accepts multiple options. Defaults to `false`.
31
+ * The label of the select field.
32
32
  */
33
- multiple: false;
33
+ label?: string;
34
34
  /**
35
- * The default value of the select.
35
+ * Additional classes to apply to the select component.
36
36
  */
37
- defaultValue: string;
38
- };
39
-
40
- type MultipleProps = {
37
+ class?: string;
41
38
  /**
42
- * Whether the select accepts multiple options. Defaults to `false`.
39
+ * The name of the select field, used for form submission.
43
40
  */
44
- multiple: true;
41
+ name?: string;
45
42
  /**
46
- * The default value of the select.
43
+ * Whether the select field is required. Defaults to `false`.
47
44
  */
48
- defaultValue: string[];
49
- };
50
-
51
- /**
52
- * The props for the select component.
53
- */
54
- type BaseProps = {
45
+ isRequired?: boolean;
55
46
  /**
56
- * The label of the select.
47
+ * The options to display in the select dropdown.
57
48
  */
58
- label?: string;
49
+ options: SearchSelectOption[];
59
50
  /**
60
- * Additional classes to apply to the select.
51
+ * Whether the select field is disabled. Defaults to `false`.
61
52
  */
62
- class?: string;
53
+ disabled?: boolean;
63
54
  /**
64
- * The name of the select. Required because of the helper.
55
+ * Whether the select field should take up the full width of its container. Defaults to `false`.
65
56
  */
66
- name?: string;
57
+ fullWidth?: boolean;
67
58
  /**
68
- * Whether the select is required. Defaults to `false`.
59
+ * The placeholder text to display when no option is selected. Defaults to `Select`.
69
60
  */
70
- isRequired?: boolean;
61
+ placeholder?: string;
62
+ }
63
+
64
+ /**
65
+ * Properties specific to single-select mode.
66
+ */
67
+ export interface SingleSearchSelectProps extends SearchSelectBaseProps {
71
68
  /**
72
- * The options to display in the select.
69
+ * Whether the select accepts multiple options.
73
70
  */
74
- options: Option[];
71
+ multiple?: false;
75
72
  /**
76
- * Whether the select is disabled. Defaults to `false`.
73
+ * The default selected value.
77
74
  */
78
- disabled?: boolean;
75
+ defaultValue?: string;
79
76
  /**
80
- * Whether the select is full width. Defaults to `false`.
77
+ * The maximum number of options that can be selected.
78
+ * This property has no effect in single-select mode.
81
79
  */
82
- fullWidth?: boolean;
80
+ max?: never;
81
+ }
82
+
83
+ /**
84
+ * Properties specific to multi-select mode.
85
+ */
86
+ export interface MultiSearchSelectProps extends SearchSelectBaseProps {
83
87
  /**
84
- * The placeholder of the select. Defaults to `Select`.
88
+ * Whether the select accepts multiple options.
85
89
  */
86
- placeholder?: string;
90
+ multiple: true;
91
+ /**
92
+ * The default selected values.
93
+ */
94
+ defaultValue?: string[];
87
95
  /**
88
- * The maximum number of options that can be selected. Defaults to `undefined`.
96
+ * The maximum number of options that can be selected.
89
97
  */
90
98
  max?: number;
91
- };
99
+ }
100
+
101
+ /**
102
+ * Union type representing all possible select component properties.
103
+ */
104
+ export type Props = SingleSearchSelectProps | MultiSearchSelectProps;
92
105
 
93
- type Props = BaseProps & (MultipleProps | NonMultipleProps);
94
106
  const {
95
107
  label,
96
108
  defaultValue,
@@ -105,7 +117,7 @@ const {
105
117
  max = undefined,
106
118
  } = Astro.props;
107
119
 
108
- let selected: Option | (Option | undefined)[] | undefined;
120
+ let selected: SearchSelectOption | (SearchSelectOption | undefined)[] | undefined;
109
121
 
110
122
  if (multiple && Array.isArray(defaultValue)) {
111
123
  selected = defaultValue.map((x) => options.find((y) => y.value === x));
@@ -136,6 +148,7 @@ const defaultLabel = selected
136
148
  role='combobox'
137
149
  aria-controls={`${name}-dropdown`}
138
150
  aria-expanded="false"
151
+ tabindex={disabled ? -1 : 0}
139
152
  label={label || ''}
140
153
  isRequired={isRequired || false}
141
154
  />
@@ -195,7 +208,7 @@ const defaultLabel = selected
195
208
  multiple && Array.isArray(selected ?? []) && (
196
209
  <div class="sui-search-select-badge-container">
197
210
  {
198
- ((selected ?? []) as Option[]).map((s) => s &&
211
+ ((selected ?? []) as SearchSelectOption[]).map((s) => s &&
199
212
  <Badge class="sui-search-select-badge" data-value={s.value} size="sm" label={s.label} iconPosition="right" icon="x-mark" />
200
213
  )
201
214
  }
@@ -329,6 +329,7 @@ function loadSearchSelects() {
329
329
  };
330
330
  const selects = document.querySelectorAll(".sui-search-select-label");
331
331
  for (const container of selects) {
332
+ if (container.dataset.initialized === "true") continue;
332
333
  const id = container.dataset.id;
333
334
  const specialContainer = Object.assign(container, {
334
335
  input: container.querySelector("input"),
@@ -365,6 +366,7 @@ function loadSearchSelects() {
365
366
  if (state.isMultipleMap[id]) {
366
367
  recalculateBadges(state, specialContainer);
367
368
  }
369
+ container.dataset.initialized = "true";
368
370
  }
369
371
  }
370
372
  document.addEventListener("astro:page-load", loadSearchSelects);
@@ -5,15 +5,15 @@ import Icon from '../Icon/Icon.astro';
5
5
  import './select.css';
6
6
 
7
7
  /**
8
- * The props for the select component.
8
+ * Represents an option in the select component.
9
9
  */
10
- interface Option {
10
+ export interface SelectOption {
11
11
  /**
12
- * The label of the option.
12
+ * The label of the option displayed to the user.
13
13
  */
14
14
  label: string;
15
15
  /**
16
- * The value of the option.
16
+ * The value of the option used for form submission.
17
17
  */
18
18
  value: string;
19
19
  /**
@@ -22,71 +22,85 @@ interface Option {
22
22
  disabled?: boolean;
23
23
  }
24
24
 
25
- type NonMultipleProps = {
26
- /**
27
- * Whether the select accepts multiple options. Defaults to `false`.
28
- */
29
- multiple: false;
30
- /**
31
- * The default value of the select.
32
- */
33
- defaultValue: string;
34
- };
35
-
36
- type MultipleProps = {
37
- /**
38
- * Whether the select accepts multiple options. Defaults to `false`.
39
- */
40
- multiple: true;
41
- /**
42
- * The default value of the select.
43
- */
44
- defaultValue: string[];
45
- };
46
-
47
25
  /**
48
- * The props for the select component.
26
+ * Base properties shared by both single and multiple select components.
49
27
  */
50
- type BaseProps = {
28
+ export interface SelectBaseProps {
51
29
  /**
52
- * The label of the select.
30
+ * The label of the select field.
53
31
  */
54
32
  label?: string;
55
33
  /**
56
- * Additional classes to apply to the select.
34
+ * Additional classes to apply to the select component.
57
35
  */
58
36
  class?: string;
59
37
  /**
60
- * The name of the select. Required because of the helper.
38
+ * The name of the select field, used for form submission.
61
39
  */
62
40
  name?: string;
63
41
  /**
64
- * Whether the select is required. Defaults to `false`.
42
+ * Whether the select field is required. Defaults to `false`.
65
43
  */
66
44
  isRequired?: boolean;
67
45
  /**
68
- * The options to display in the select.
46
+ * The options to display in the select dropdown.
69
47
  */
70
- options: Option[];
48
+ options: SelectOption[];
71
49
  /**
72
- * Whether the select is disabled. Defaults to `false`.
50
+ * Whether the select field is disabled. Defaults to `false`.
73
51
  */
74
52
  disabled?: boolean;
75
53
  /**
76
- * Whether the select is full width. Defaults to `false`.
54
+ * Whether the select field should take up the full width of its container. Defaults to `false`.
77
55
  */
78
56
  fullWidth?: boolean;
79
57
  /**
80
- * The placeholder of the select. Defaults to `Select`.
58
+ * The placeholder text to display when no option is selected. Defaults to `Select`.
81
59
  */
82
60
  placeholder?: string;
61
+ }
62
+
63
+ /**
64
+ * Properties specific to single-select mode.
65
+ */
66
+ export interface SingleSelectProps extends SelectBaseProps {
67
+ /**
68
+ * Whether the select accepts multiple options.
69
+ */
70
+ multiple?: false;
83
71
  /**
84
- * The maximum number of options that can be selected. Defaults to `undefined`.
72
+ * The default selected value.
73
+ */
74
+ defaultValue?: string;
75
+ /**
76
+ * The maximum number of options that can be selected.
77
+ * This property has no effect in single-select mode.
78
+ */
79
+ max?: never;
80
+ }
81
+
82
+ /**
83
+ * Properties specific to multi-select mode.
84
+ */
85
+ export interface MultiSelectProps extends SelectBaseProps {
86
+ /**
87
+ * Whether the select accepts multiple options.
88
+ */
89
+ multiple: true;
90
+ /**
91
+ * The default selected values.
92
+ */
93
+ defaultValue?: string[];
94
+ /**
95
+ * The maximum number of options that can be selected.
85
96
  */
86
97
  max?: number;
87
- };
98
+ }
88
99
 
89
- type Props = BaseProps & (MultipleProps | NonMultipleProps);
100
+ /**
101
+ * Union type representing all possible select component properties.
102
+ */
103
+ export type Props = SingleSelectProps | MultiSelectProps;
90
104
 
91
105
  const {
92
106
  label,
@@ -102,7 +116,7 @@ const {
102
116
  max = undefined,
103
117
  } = Astro.props;
104
118
 
105
- let selected: Option | (Option | undefined)[] | undefined;
119
+ let selected: SelectOption | (SelectOption | undefined)[] | undefined;
106
120
 
107
121
  if (multiple && Array.isArray(defaultValue)) {
108
122
  selected = defaultValue.map((x) => options.find((y) => y.value === x));
@@ -139,6 +153,7 @@ const defaultLabel = selected
139
153
  aria-controls={`${name}-dropdown`}
140
154
  aria-expanded="false"
141
155
  id={`${name}-select-btn`}
156
+ tabindex={disabled ? -1 : 0}
142
157
  type="button"
143
158
  aria-label={placeholder}
144
159
  title={placeholder}
@@ -328,6 +328,7 @@ function loadSelects() {
328
328
  }
329
329
  });
330
330
  for (const container of selects) {
331
+ if (container.dataset.initialized === "true") continue;
331
332
  const id = container.dataset.id;
332
333
  const specialContainer = Object.assign(container, {
333
334
  button: container.querySelector("button"),
@@ -351,6 +352,7 @@ function loadSelects() {
351
352
  });
352
353
  handleBadgeOverflow(state, specialContainer);
353
354
  }
355
+ container.dataset.initialized = "true";
354
356
  }
355
357
  }
356
358
  document.addEventListener("astro:page-load", loadSelects);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@studiocms/ui",
3
- "version": "0.4.14",
3
+ "version": "0.4.15",
4
4
  "description": "The UI library for StudioCMS. Includes the layouts & components we use to build StudioCMS.",
5
5
  "repository": {
6
6
  "type": "git",