@aiaiai-pt/design-system 0.22.0 → 0.23.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.
@@ -0,0 +1,111 @@
1
+ <!--
2
+ @component NotificationPrefs
3
+
4
+ The citizen account area's notification-preferences panel — a toggle list over
5
+ a citizen's subscriptions. Vertical-agnostic: `items` is already shaped to
6
+ `{ id, label, description?, active }`; the consumer (a portal widget over the
7
+ account subscription lane) maps the backend rows and owns the persistence.
8
+ Toggling a row calls `onToggle(id, next)` — the consumer performs the write
9
+ and re-feeds `items` (or sets `busyId` while it's in flight).
10
+
11
+ Accessibility-first: a bordered semantic list inside a `<section>` region
12
+ named by `label` (the visible page heading is the consumer's job — same split
13
+ as RankingBoard, so one widget can own the page `<h1>`). Each row pairs a real
14
+ text label (and optional description) with a `Toggle` (`role="switch"`,
15
+ `aria-checked`); the switch carries its own accessible name via `aria-label`.
16
+ A row that's saving is `disabled` (no double-submit). Consumes semantic tokens
17
+ so dark / high-contrast schemes (#244) ride through. Soft-empty: no items →
18
+ renders the `emptyText` note (never an empty shell).
19
+
20
+ @example
21
+ <NotificationPrefs
22
+ label="Notifications"
23
+ items={[
24
+ { id: "all", label: "All updates", description: "Every proposal and phase change", active: true },
25
+ { id: "sub-2", label: "Cycling lane", active: false },
26
+ ]}
27
+ onToggle={(id, next) => save(id, next)}
28
+ busyId={savingId}
29
+ />
30
+ -->
31
+ <script module>
32
+ /**
33
+ * @typedef {{ id: string, label: string, description?: string, active: boolean }} PrefItem
34
+ */
35
+ </script>
36
+
37
+ <script>
38
+ import List from "./List.svelte";
39
+ import ListItem from "./ListItem.svelte";
40
+ import Toggle from "./Toggle.svelte";
41
+
42
+ let {
43
+ /** @type {PrefItem[]} The subscription rows, already mapped to labels. */
44
+ items = [],
45
+ /** @type {string} Accessible name for the preferences region (localize it). */
46
+ label = "Notification preferences",
47
+ /** @type {string} Shown when there are no items (localize it). */
48
+ emptyText = "You have no notification preferences yet.",
49
+ /** @type {((id: string, active: boolean) => void) | undefined} Called on toggle. */
50
+ onToggle = undefined,
51
+ /** @type {string | null} The row currently saving — its toggle disables. */
52
+ busyId = null,
53
+ /** @type {string} */
54
+ class: className = "",
55
+ ...rest
56
+ } = $props();
57
+ </script>
58
+
59
+ <section class="notification-prefs {className}" aria-label={label} {...rest}>
60
+ {#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>
81
+ {:else}
82
+ <p class="notification-prefs-empty">{emptyText}</p>
83
+ {/if}
84
+ </section>
85
+
86
+ <style>
87
+ .notification-prefs {
88
+ display: flex;
89
+ flex-direction: column;
90
+ gap: var(--space-md);
91
+ }
92
+
93
+ .notification-prefs-label {
94
+ font-family: var(--type-body-font);
95
+ font-size: var(--type-body-size);
96
+ color: var(--color-text);
97
+ }
98
+
99
+ .notification-prefs-desc {
100
+ font-family: var(--type-body-sm-font);
101
+ font-size: var(--type-body-sm-size);
102
+ color: var(--color-text-muted);
103
+ }
104
+
105
+ .notification-prefs-empty {
106
+ font-family: var(--type-body-font);
107
+ font-size: var(--type-body-size);
108
+ color: var(--color-text-muted);
109
+ margin: 0;
110
+ }
111
+ </style>
@@ -0,0 +1,55 @@
1
+ export default NotificationPrefs;
2
+ export type PrefItem = {
3
+ id: string;
4
+ label: string;
5
+ description?: string;
6
+ active: boolean;
7
+ };
8
+ type NotificationPrefs = {
9
+ $on?(type: string, callback: (e: any) => void): () => void;
10
+ $set?(props: Partial<$$ComponentProps>): void;
11
+ };
12
+ /**
13
+ * NotificationPrefs
14
+ *
15
+ * The citizen account area's notification-preferences panel — a toggle list over
16
+ * a citizen's subscriptions. Vertical-agnostic: `items` is already shaped to
17
+ * `{ id, label, description?, active }`; the consumer (a portal widget over the
18
+ * account subscription lane) maps the backend rows and owns the persistence.
19
+ * Toggling a row calls `onToggle(id, next)` — the consumer performs the write
20
+ * and re-feeds `items` (or sets `busyId` while it's in flight).
21
+ *
22
+ * Accessibility-first: a bordered semantic list; each row pairs a real text
23
+ * label (and optional description) with a `Toggle` (`role="switch"`,
24
+ * `aria-checked`). The switch carries an accessible name via `aria-label` so the
25
+ * control reads on its own. A row that's saving is `disabled` (no double-submit).
26
+ * Consumes semantic tokens so dark / high-contrast schemes (#244) ride through.
27
+ * Soft-empty: no items → renders the `emptyText` note (never an empty shell).
28
+ *
29
+ * @example
30
+ * <NotificationPrefs
31
+ * heading="Notifications"
32
+ * items={[
33
+ * { id: "all", label: "All updates", description: "Every proposal and phase change", active: true },
34
+ * { id: "sub-2", label: "Cycling lane", active: false },
35
+ * ]}
36
+ * onToggle={(id, next) => save(id, next)}
37
+ * busyId={savingId}
38
+ * />
39
+ */
40
+ declare const NotificationPrefs: import("svelte").Component<{
41
+ items?: any[];
42
+ heading?: string;
43
+ emptyText?: string;
44
+ onToggle?: any;
45
+ busyId?: any;
46
+ class?: string;
47
+ } & Record<string, any>, {}, "">;
48
+ type $$ComponentProps = {
49
+ items?: any[];
50
+ heading?: string;
51
+ emptyText?: string;
52
+ onToggle?: any;
53
+ busyId?: any;
54
+ class?: string;
55
+ } & Record<string, any>;
@@ -90,6 +90,10 @@ export { default as PhaseTimeline } from "./PhaseTimeline.svelte";
90
90
  export { default as RankingBoard } from "./RankingBoard.svelte";
91
91
  export { default as ResultsChart } from "./ResultsChart.svelte";
92
92
 
93
+ // Account / preferences (#105 Phase 5) — the citizen account area's
94
+ // notification-preferences panel: a toggle list over a citizen's subscriptions.
95
+ export { default as NotificationPrefs } from "./NotificationPrefs.svelte";
96
+
93
97
  // Feedback
94
98
  export { default as Alert } from "./Alert.svelte";
95
99
  export { default as Toast } from "./Toast.svelte";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiaiai-pt/design-system",
3
- "version": "0.22.0",
3
+ "version": "0.23.0",
4
4
  "description": "Design system tokens and Svelte components for aiaiai products",
5
5
  "license": "MIT",
6
6
  "type": "module",