@aiaiai-pt/design-system 0.2.3 → 0.3.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,204 @@
1
+ <!--
2
+ @component ConditionTable
3
+
4
+ Configurable condition-row editor for building rule-based filters.
5
+ Composes Input, Select, and Button primitives into an interactive grid.
6
+
7
+ @example
8
+ <ConditionTable
9
+ columns={[
10
+ { key: 'field', label: 'Field', type: 'text', placeholder: 'data.field_name' },
11
+ { key: 'operator', label: 'Operator', type: 'select', width: '10rem', options: operators },
12
+ { key: 'value', label: 'Value', type: 'text', placeholder: 'value' },
13
+ ]}
14
+ bind:conditions
15
+ onchange={handleChange}
16
+ />
17
+ -->
18
+ <script>
19
+ /**
20
+ * @typedef {{ value: string, label: string }} SelectOption
21
+ * @typedef {{ key: string, label: string, type: 'text' | 'select', width?: string, options?: SelectOption[], placeholder?: string }} ColumnDef
22
+ */
23
+
24
+ import Input from './Input.svelte';
25
+ import Select from './Select.svelte';
26
+ import Button from './Button.svelte';
27
+
28
+ let {
29
+ /** @type {{ [key: string]: string }[]} */
30
+ conditions = $bindable([]),
31
+ /** @type {ColumnDef[]} */
32
+ columns = [],
33
+ /** @type {number} */
34
+ maxRows = 20,
35
+ /** @type {boolean} */
36
+ disabled = false,
37
+ /** @type {string} */
38
+ emptyMessage = 'No conditions defined.',
39
+ /** @type {string} */
40
+ addLabel = 'Add condition',
41
+ /** @type {((conditions: { [key: string]: string }[]) => void) | undefined} */
42
+ onchange = undefined,
43
+ /** @type {string} */
44
+ class: className = '',
45
+ ...rest
46
+ } = $props();
47
+
48
+ const gridTemplate = $derived(
49
+ columns.map((col) => col.width ?? '1fr').join(' ') + ' 2.5rem'
50
+ );
51
+
52
+ const canAdd = $derived(conditions.length < maxRows);
53
+
54
+ function notify() {
55
+ onchange?.(conditions);
56
+ }
57
+
58
+ function addRow() {
59
+ /** @type {{ [key: string]: string }} */
60
+ const row = {};
61
+ for (const col of columns) {
62
+ row[col.key] = '';
63
+ }
64
+ conditions = [...conditions, row];
65
+ notify();
66
+ }
67
+
68
+ /** @param {number} index */
69
+ function removeRow(index) {
70
+ conditions = conditions.filter((_, i) => i !== index);
71
+ notify();
72
+ }
73
+
74
+ /**
75
+ * @param {number} rowIndex
76
+ * @param {string} key
77
+ * @param {string} value
78
+ */
79
+ function updateCell(rowIndex, key, value) {
80
+ conditions[rowIndex] = { ...conditions[rowIndex], [key]: value };
81
+ conditions = [...conditions];
82
+ notify();
83
+ }
84
+ </script>
85
+
86
+ <div class="condition-table {className}" {...rest}>
87
+ {#if conditions.length === 0}
88
+ <p class="condition-table-empty">{emptyMessage}</p>
89
+ {:else}
90
+ <div class="condition-table-grid" style:grid-template-columns={gridTemplate}>
91
+ <!-- Header -->
92
+ {#each columns as col}
93
+ <span class="condition-table-header">{col.label}</span>
94
+ {/each}
95
+ <span class="condition-table-header"></span>
96
+
97
+ <!-- Rows -->
98
+ {#each conditions as row, rowIndex}
99
+ {#each columns as col}
100
+ <div class="condition-table-cell">
101
+ {#if col.type === 'select'}
102
+ <Select
103
+ size="sm"
104
+ value={row[col.key] ?? ''}
105
+ options={col.options ?? []}
106
+ placeholder={col.placeholder}
107
+ {disabled}
108
+ onchange={(e) => updateCell(rowIndex, col.key, e.target.value)}
109
+ />
110
+ {:else}
111
+ <Input
112
+ size="sm"
113
+ value={row[col.key] ?? ''}
114
+ placeholder={col.placeholder}
115
+ {disabled}
116
+ oninput={(e) => updateCell(rowIndex, col.key, e.target.value)}
117
+ />
118
+ {/if}
119
+ </div>
120
+ {/each}
121
+ <div class="condition-table-cell condition-table-delete">
122
+ <Button
123
+ variant="ghost"
124
+ size="sm"
125
+ iconOnly
126
+ {disabled}
127
+ aria-label="Remove condition"
128
+ onclick={() => removeRow(rowIndex)}
129
+ >
130
+ {#snippet icon()}
131
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
132
+ <path d="M4 4L12 12M12 4L4 12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
133
+ </svg>
134
+ {/snippet}
135
+ </Button>
136
+ </div>
137
+ {/each}
138
+ </div>
139
+ {/if}
140
+
141
+ {#if canAdd}
142
+ <div class="condition-table-footer">
143
+ <Button
144
+ variant="ghost"
145
+ size="sm"
146
+ {disabled}
147
+ onclick={addRow}
148
+ >
149
+ {#snippet icon()}
150
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true">
151
+ <path d="M8 3V13M3 8H13" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
152
+ </svg>
153
+ {/snippet}
154
+ {addLabel}
155
+ </Button>
156
+ </div>
157
+ {/if}
158
+ </div>
159
+
160
+ <style>
161
+ .condition-table {
162
+ display: flex;
163
+ flex-direction: column;
164
+ gap: var(--condition-table-row-gap);
165
+ width: 100%;
166
+ }
167
+
168
+ .condition-table-grid {
169
+ display: grid;
170
+ row-gap: var(--condition-table-row-gap);
171
+ column-gap: var(--condition-table-header-gap);
172
+ align-items: center;
173
+ }
174
+
175
+ .condition-table-header {
176
+ font-family: var(--input-label-font);
177
+ font-size: var(--input-label-size);
178
+ letter-spacing: var(--input-label-tracking);
179
+ color: var(--input-label-color);
180
+ user-select: none;
181
+ }
182
+
183
+ .condition-table-cell {
184
+ min-width: 0;
185
+ }
186
+
187
+ .condition-table-delete {
188
+ display: flex;
189
+ align-items: center;
190
+ justify-content: center;
191
+ }
192
+
193
+ .condition-table-empty {
194
+ font-family: var(--input-help-font);
195
+ font-size: var(--input-help-size);
196
+ color: var(--condition-table-empty-color);
197
+ margin: 0;
198
+ padding: var(--space-sm) 0;
199
+ }
200
+
201
+ .condition-table-footer {
202
+ padding-top: var(--space-xs);
203
+ }
204
+ </style>
@@ -68,3 +68,4 @@ export { default as CodeBlock } from "./CodeBlock.svelte";
68
68
  export { default as CodeEditor } from "./CodeEditor.svelte";
69
69
  export { default as CollapsibleSection } from "./CollapsibleSection.svelte";
70
70
  export { default as OptionGrid } from "./OptionGrid.svelte";
71
+ export { default as ConditionTable } from "./ConditionTable.svelte";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aiaiai-pt/design-system",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "Design system tokens and Svelte components for aiaiai products",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -32,14 +32,14 @@
32
32
  "tokens"
33
33
  ],
34
34
  "peerDependencies": {
35
- "svelte": "^5.0.0",
36
- "@codemirror/view": "^6.0.0",
37
- "@codemirror/state": "^6.0.0",
38
- "@codemirror/language": "^6.0.0",
39
- "@codemirror/commands": "^6.0.0",
40
- "@codemirror/lang-sql": "^6.0.0",
41
- "@codemirror/lang-python": "^6.0.0",
42
- "@codemirror/lang-json": "^6.0.0",
43
- "@lezer/highlight": "^1.0.0"
35
+ "@codemirror/commands": "^6.10.3",
36
+ "@codemirror/lang-json": "^6.0.2",
37
+ "@codemirror/lang-python": "^6.2.1",
38
+ "@codemirror/lang-sql": "^6.10.0",
39
+ "@codemirror/language": "^6.12.2",
40
+ "@codemirror/state": "^6.6.0",
41
+ "@codemirror/view": "^6.40.0",
42
+ "@lezer/highlight": "^1.0.0",
43
+ "svelte": "^5.0.0"
44
44
  }
45
45
  }
@@ -524,6 +524,14 @@
524
524
  --combobox-description-color: var(--color-text-muted);
525
525
  --combobox-empty-color: var(--color-text-muted);
526
526
 
527
+ /* ═══════════════════════════════════════════════
528
+ CONDITION TABLE
529
+ ═══════════════════════════════════════════════ */
530
+
531
+ --condition-table-header-gap: var(--space-xs);
532
+ --condition-table-row-gap: var(--space-xs);
533
+ --condition-table-empty-color: var(--color-text-secondary);
534
+
527
535
  /* ═══════════════════════════════════════════════
528
536
  FOCUS RING (global)
529
537
  ═══════════════════════════════════════════════ */