@streamscloud/kit 0.25.1 → 0.25.2

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.
@@ -13,18 +13,22 @@ const isSelected = (candidate) => value.some((selected) => eq(selected, candidat
13
13
  const withValue = (list, candidate) => (list.some((item) => eq(item, candidate)) ? list : [...list, candidate]);
14
14
  const without = (list, candidate) => list.filter((item) => !eq(item, candidate));
15
15
  const optionDisplay = $derived(single ? 'checkmark' : 'checkbox');
16
- const leaves = $derived(options.flatMap((item) => (isSelectGroup(item) ? item.options : [item])));
17
- const selectedLeaves = $derived(leaves.filter((leaf) => isSelected(leaf.value)));
16
+ const selectedOptions = $derived(options
17
+ .flatMap((item) => isSelectGroup(item) ? (item.value !== undefined ? [{ label: item.label, value: item.value }, ...item.options] : item.options) : [item])
18
+ .filter((option) => isSelected(option.value)));
18
19
  const summary = $derived.by(() => {
19
- if (selectedLeaves.length === 0) {
20
+ if (selectedOptions.length === 0) {
20
21
  return label;
21
22
  }
22
- if (selectedLeaves.length === 1) {
23
- return `${label}: ${selectedLeaves[0].label}`;
23
+ if (selectedOptions.length === 1) {
24
+ return `${label}: ${selectedOptions[0].label}`;
24
25
  }
25
- return `${label}: ${localization.selectedCount(selectedLeaves.length)}`;
26
+ return `${label}: ${localization.selectedCount(selectedOptions.length)}`;
26
27
  });
27
28
  const groupSelected = (group) => {
29
+ if (group.value !== undefined) {
30
+ return isSelected(group.value);
31
+ }
28
32
  const selectedCount = group.options.filter((option) => isSelected(option.value)).length;
29
33
  if (selectedCount === 0) {
30
34
  return false;
@@ -41,7 +45,17 @@ const toggleOption = (option) => {
41
45
  emit(isSelected(option.value) ? without(value, option.value) : withValue(value, option.value));
42
46
  };
43
47
  const toggleGroup = (group) => {
48
+ if (group.value !== undefined) {
49
+ if (single) {
50
+ pickSingle(group.value);
51
+ return;
52
+ }
53
+ emit(isSelected(group.value) ? without(value, group.value) : withValue(value, group.value));
54
+ return;
55
+ }
44
56
  if (single) {
57
+ const hasSelectedChild = group.options.some((option) => isSelected(option.value));
58
+ emit(hasSelectedChild ? [] : group.options.slice(0, 1).map((option) => option.value));
45
59
  return;
46
60
  }
47
61
  const allSelected = group.options.every((option) => isSelected(option.value));
@@ -49,22 +63,14 @@ const toggleGroup = (group) => {
49
63
  for (const option of group.options) {
50
64
  next = allSelected ? without(next, option.value) : withValue(next, option.value);
51
65
  }
52
- if (group.value !== undefined) {
53
- next = allSelected ? without(next, group.value) : withValue(next, group.value);
54
- }
55
66
  emit(next);
56
67
  };
57
- const toggleChild = (group, child) => {
68
+ const toggleChild = (child) => {
58
69
  if (single) {
59
70
  pickSingle(child.value);
60
71
  return;
61
72
  }
62
- let next = isSelected(child.value) ? without(value, child.value) : withValue(value, child.value);
63
- if (group.value !== undefined) {
64
- const anyChildSelected = group.options.some((option) => next.some((selected) => eq(selected, option.value)));
65
- next = anyChildSelected ? withValue(next, group.value) : without(next, group.value);
66
- }
67
- emit(next);
73
+ emit(isSelected(child.value) ? without(value, child.value) : withValue(value, child.value));
68
74
  };
69
75
  const clearAll = () => emit([]);
70
76
  </script>
@@ -75,7 +81,7 @@ const clearAll = () => emit([]);
75
81
  {#if header}
76
82
  {@render header()}
77
83
  {/if}
78
- {#each options as item (isSelectGroup(item) ? item.label : item.value)}
84
+ {#each options as item (isSelectGroup(item) ? (item.value ?? item.options[0]?.value ?? item.label) : item.value)}
79
85
  {#if isSelectGroup(item)}
80
86
  <ToolbarOption
81
87
  size={optionSize}
@@ -87,7 +93,7 @@ const clearAll = () => emit([]);
87
93
  size={optionSize}
88
94
  indent
89
95
  selectable={{ selected: isSelected(child.value), display: optionDisplay }}
90
- on={{ click: () => toggleChild(item, child) }}>{child.label}</ToolbarOption>
96
+ on={{ click: () => toggleChild(child) }}>{child.label}</ToolbarOption>
91
97
  {/each}
92
98
  {:else}
93
99
  <ToolbarOption size={optionSize} selectable={{ selected: isSelected(item.value), display: optionDisplay }} on={{ click: () => toggleOption(item) }}
@@ -119,8 +125,11 @@ const clearAll = () => emit([]);
119
125
  A compact toolbar filter dropdown: a secondary-button trigger whose label summarises the selection
120
126
  (`Status` → `Status: Draft` → `Status: 2 selected`), and a `ToolbarOption` checklist panel with a
121
127
  `Clear all` footer. Multi-select by default; `single` restricts to one (checkmark glyph). `options` is a
122
- flat `SelectOption[]` or one level of groups (`SelectOptionGroup`) for a tree with a tri-state parent
123
- row and `children-include-parent` cascade.
128
+ flat `SelectOption[]` or one level of groups (`SelectOptionGroup`). Group behaviour depends on whether
129
+ the group has its own `value`: a label-group (no `value`) is a tri-state select-all header over its
130
+ children; a group WITH a `value` is an independent option — header and children toggle only themselves,
131
+ with no cascade or shared highlight. In `single` mode a value-group header selects its own value (re-click
132
+ deselects), same as any option.
124
133
  `header` injects content above the list (preset rows, a `ToolbarSectionTitle`, …).
125
134
 
126
135
  ### CSS Custom Properties
@@ -54,8 +54,11 @@ interface $$IsomorphicComponent {
54
54
  * A compact toolbar filter dropdown: a secondary-button trigger whose label summarises the selection
55
55
  * (`Status` → `Status: Draft` → `Status: 2 selected`), and a `ToolbarOption` checklist panel with a
56
56
  * `Clear all` footer. Multi-select by default; `single` restricts to one (checkmark glyph). `options` is a
57
- * flat `SelectOption[]` or one level of groups (`SelectOptionGroup`) for a tree with a tri-state parent
58
- * row and `children-include-parent` cascade.
57
+ * flat `SelectOption[]` or one level of groups (`SelectOptionGroup`). Group behaviour depends on whether
58
+ * the group has its own `value`: a label-group (no `value`) is a tri-state select-all header over its
59
+ * children; a group WITH a `value` is an independent option — header and children toggle only themselves,
60
+ * with no cascade or shared highlight. In `single` mode a value-group header selects its own value (re-click
61
+ * deselects), same as any option.
59
62
  * `header` injects content above the list (preset rows, a `ToolbarSectionTitle`, …).
60
63
  *
61
64
  * ### CSS Custom Properties
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamscloud/kit",
3
- "version": "0.25.1",
3
+ "version": "0.25.2",
4
4
  "author": "StreamsCloud",
5
5
  "repository": {
6
6
  "type": "git",