@aiaiai-pt/design-system 0.2.3 → 0.3.1
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.
- package/components/ConditionTable.svelte +206 -0
- package/components/index.js +1 -0
- package/package.json +10 -10
- package/tokens/components.css +8 -0
|
@@ -0,0 +1,206 @@
|
|
|
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
|
+
overflow-x: auto;
|
|
174
|
+
-webkit-overflow-scrolling: touch;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.condition-table-header {
|
|
178
|
+
font-family: var(--input-label-font);
|
|
179
|
+
font-size: var(--input-label-size);
|
|
180
|
+
letter-spacing: var(--input-label-tracking);
|
|
181
|
+
color: var(--input-label-color);
|
|
182
|
+
user-select: none;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.condition-table-cell {
|
|
186
|
+
min-width: 0;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.condition-table-delete {
|
|
190
|
+
display: flex;
|
|
191
|
+
align-items: center;
|
|
192
|
+
justify-content: center;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.condition-table-empty {
|
|
196
|
+
font-family: var(--input-help-font);
|
|
197
|
+
font-size: var(--input-help-size);
|
|
198
|
+
color: var(--condition-table-empty-color);
|
|
199
|
+
margin: 0;
|
|
200
|
+
padding: var(--space-sm) 0;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.condition-table-footer {
|
|
204
|
+
padding-top: var(--space-xs);
|
|
205
|
+
}
|
|
206
|
+
</style>
|
package/components/index.js
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "0.3.1",
|
|
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
|
-
"
|
|
36
|
-
"@codemirror/
|
|
37
|
-
"@codemirror/
|
|
38
|
-
"@codemirror/
|
|
39
|
-
"@codemirror/
|
|
40
|
-
"@codemirror/
|
|
41
|
-
"@codemirror/
|
|
42
|
-
"@
|
|
43
|
-
"
|
|
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
|
}
|
package/tokens/components.css
CHANGED
|
@@ -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-sm);
|
|
532
|
+
--condition-table-row-gap: var(--space-sm);
|
|
533
|
+
--condition-table-empty-color: var(--color-text-secondary);
|
|
534
|
+
|
|
527
535
|
/* ═══════════════════════════════════════════════
|
|
528
536
|
FOCUS RING (global)
|
|
529
537
|
═══════════════════════════════════════════════ */
|