@aiaiai-pt/design-system 0.28.0 → 0.29.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.
@@ -30,7 +30,7 @@
30
30
  -->
31
31
  <script module>
32
32
  /**
33
- * @typedef {{ id: string, label: string, description?: string, active: boolean }} PrefItem
33
+ * @typedef {{ id: string, label: string, description?: string, active: boolean, group?: string }} PrefItem
34
34
  */
35
35
  </script>
36
36
 
@@ -54,30 +54,56 @@
54
54
  class: className = "",
55
55
  ...rest
56
56
  } = $props();
57
+
58
+ // Group rows by their optional `group` (order preserved). Items with no
59
+ // `group` fall in the leading unnamed group, which renders WITHOUT a header —
60
+ // so a flat `items` list (no group set) renders exactly as before
61
+ // (backward-compatible). A named group renders an <h3> section header above
62
+ // its bordered list (e.g. "Edição" / "Categoria" / "Freguesia").
63
+ const groups = $derived.by(() => {
64
+ /** @type {string[]} */
65
+ const order = [];
66
+ /** @type {Map<string, PrefItem[]>} */
67
+ const byGroup = new Map();
68
+ for (const item of items) {
69
+ const g = item.group ?? "";
70
+ if (!byGroup.has(g)) {
71
+ byGroup.set(g, []);
72
+ order.push(g);
73
+ }
74
+ byGroup.get(g)?.push(item);
75
+ }
76
+ return order.map((g) => ({ group: g, items: byGroup.get(g) ?? [] }));
77
+ });
57
78
  </script>
58
79
 
59
80
  <section class="notification-prefs {className}" aria-label={label} {...rest}>
60
81
  {#if items.length > 0}
61
- <List variant="bordered">
62
- {#each items as item (item.id)}
63
- <ListItem>
64
- {#snippet leading()}
65
- <span class="notification-prefs-label">{item.label}</span>
66
- {#if item.description}
67
- <span class="notification-prefs-desc">{item.description}</span>
68
- {/if}
69
- {/snippet}
70
- {#snippet trailing()}
71
- <Toggle
72
- checked={item.active}
73
- disabled={busyId === item.id}
74
- aria-label={item.label}
75
- onchange={(next) => onToggle?.(item.id, next)}
76
- />
77
- {/snippet}
78
- </ListItem>
79
- {/each}
80
- </List>
82
+ {#each groups as grp (grp.group)}
83
+ {#if grp.group}
84
+ <h3 class="notification-prefs-group">{grp.group}</h3>
85
+ {/if}
86
+ <List variant="bordered">
87
+ {#each grp.items as item (item.id)}
88
+ <ListItem>
89
+ {#snippet leading()}
90
+ <span class="notification-prefs-label">{item.label}</span>
91
+ {#if item.description}
92
+ <span class="notification-prefs-desc">{item.description}</span>
93
+ {/if}
94
+ {/snippet}
95
+ {#snippet trailing()}
96
+ <Toggle
97
+ checked={item.active}
98
+ disabled={busyId === item.id}
99
+ aria-label={item.label}
100
+ onchange={(next) => onToggle?.(item.id, next)}
101
+ />
102
+ {/snippet}
103
+ </ListItem>
104
+ {/each}
105
+ </List>
106
+ {/each}
81
107
  {:else}
82
108
  <p class="notification-prefs-empty">{emptyText}</p>
83
109
  {/if}
@@ -90,6 +116,18 @@
90
116
  gap: var(--space-md);
91
117
  }
92
118
 
119
+ .notification-prefs-group {
120
+ font-family: var(--type-heading-font, var(--type-body-font));
121
+ font-size: var(--type-body-size);
122
+ font-weight: 600;
123
+ color: var(--color-text);
124
+ margin: var(--space-sm) 0 calc(-1 * var(--space-xs)) 0;
125
+ }
126
+
127
+ .notification-prefs-group:first-child {
128
+ margin-top: 0;
129
+ }
130
+
93
131
  .notification-prefs-label {
94
132
  font-family: var(--type-body-font);
95
133
  font-size: var(--type-body-size);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiaiai-pt/design-system",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "description": "Design system tokens and Svelte components for aiaiai products",
5
5
  "license": "MIT",
6
6
  "type": "module",