@keenmate/pure-admin-core 2.4.0 → 2.5.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.
Files changed (44) hide show
  1. package/README.md +11 -6
  2. package/dist/css/main.css +47 -130
  3. package/package.json +1 -1
  4. package/snippets/AUDIT.md +94 -0
  5. package/snippets/alerts.html +264 -89
  6. package/snippets/badges.html +193 -61
  7. package/snippets/buttons.html +178 -0
  8. package/snippets/callouts.html +210 -129
  9. package/snippets/cards.html +383 -200
  10. package/snippets/checkbox-lists.html +199 -65
  11. package/snippets/code.html +55 -11
  12. package/snippets/command-palette.html +401 -111
  13. package/snippets/comparison.html +144 -93
  14. package/snippets/customization.html +311 -104
  15. package/snippets/data-display.html +584 -0
  16. package/snippets/detail-panel.html +470 -138
  17. package/snippets/filter-card.html +246 -0
  18. package/snippets/forms.html +408 -308
  19. package/snippets/grid.html +253 -141
  20. package/snippets/layout.html +379 -480
  21. package/snippets/lists.html +144 -47
  22. package/snippets/loaders.html +64 -39
  23. package/snippets/manifest.json +330 -280
  24. package/snippets/modal-dialogs.html +137 -64
  25. package/snippets/modals.html +221 -151
  26. package/snippets/notifications.html +285 -0
  27. package/snippets/popconfirm.html +213 -19
  28. package/snippets/profile.html +290 -330
  29. package/snippets/statistics.html +247 -0
  30. package/snippets/tables.html +359 -150
  31. package/snippets/tabs.html +129 -45
  32. package/snippets/timeline.html +123 -56
  33. package/snippets/toasts.html +179 -31
  34. package/snippets/tooltips.html +199 -81
  35. package/snippets/typography.html +183 -58
  36. package/snippets/utilities.html +511 -415
  37. package/snippets/virtual-scroll.html +201 -75
  38. package/snippets/web-daterangepicker.html +369 -189
  39. package/snippets/web-multiselect.html +360 -124
  40. package/src/scss/core-components/_alerts.scss +51 -12
  41. package/src/scss/core-components/_pagers.scss +1 -1
  42. package/src/scss/core-components/_popconfirm.scss +35 -13
  43. package/src/scss/core-components/_tables.scss +2 -134
  44. package/src/scss/variables/_components.scss +17 -2
@@ -1,160 +1,177 @@
1
1
  <!-- ================================
2
2
  FORM SNIPPETS
3
3
  Pure Admin Visual Framework
4
+
5
+ Form SCSS lives in core-components/forms/:
6
+ _form-layout.scss .pa-form, .pa-form-group
7
+ _form-inputs.scss .pa-input, .pa-select, .pa-textarea + sizes
8
+ _form-states.scss validation states + theme colour slots
9
+ _input-groups.scss .pa-input-group + prepend/append/button
10
+ _input-wrapper.scss .pa-input-wrapper (clear button), search tokens
11
+ _checkboxes-radios.scss .pa-checkbox (tri-state) + .pa-radio
12
+ _query-editor.scss search highlighting + inline query editor
13
+ (complex enough for its own future snippet)
4
14
  ================================ -->
5
15
 
16
+
6
17
  <!-- BASIC FORM ELEMENTS -->
7
18
 
8
- <!-- Text Input -->
9
- <div class="pa-form-group">
10
- <label class="pa-form-label" for="username">Username</label>
11
- <input type="text" class="pa-input" id="username" placeholder="Enter username">
12
- </div>
19
+ <!--
20
+ Label styling: when a <label> lives inside .pa-form > .pa-form-group,
21
+ the SCSS styles it automatically (flex, gap, medium weight, small
22
+ font). No .pa-form-label class is needed — just use native <label>.
23
+ -->
13
24
 
14
- <!-- Email Input -->
15
- <div class="pa-form-group">
16
- <label class="pa-form-label" for="email">Email</label>
17
- <input type="email" class="pa-input" id="email" placeholder="your@email.com">
18
- </div>
25
+ <form class="pa-form">
26
+ <div class="pa-form-group">
27
+ <label for="username">Username</label>
28
+ <input type="text" class="pa-input" id="username" placeholder="Enter username">
29
+ </div>
19
30
 
20
- <!-- Password Input -->
21
- <div class="pa-form-group">
22
- <label class="pa-form-label" for="password">Password</label>
23
- <input type="password" class="pa-input" id="password" placeholder="Enter password">
24
- </div>
31
+ <div class="pa-form-group">
32
+ <label for="email">Email</label>
33
+ <input type="email" class="pa-input" id="email" placeholder="your@email.com">
34
+ </div>
25
35
 
26
- <!-- Textarea -->
27
- <div class="pa-form-group">
28
- <label class="pa-form-label" for="message">Message</label>
29
- <textarea class="pa-textarea" id="message" rows="4" placeholder="Enter your message"></textarea>
30
- </div>
36
+ <div class="pa-form-group">
37
+ <label for="password">Password</label>
38
+ <input type="password" class="pa-input" id="password" placeholder="Enter password">
39
+ </div>
31
40
 
32
- <!-- Select -->
33
- <div class="pa-form-group">
34
- <label class="pa-form-label" for="country">Country</label>
35
- <select class="pa-select" id="country">
36
- <option>United States</option>
37
- <option>United Kingdom</option>
38
- <option>Canada</option>
39
- </select>
40
- </div>
41
+ <div class="pa-form-group">
42
+ <label for="message">Message</label>
43
+ <textarea class="pa-textarea" id="message" rows="4" placeholder="Enter your message"></textarea>
44
+ </div>
41
45
 
46
+ <div class="pa-form-group">
47
+ <label for="country">Country</label>
48
+ <select class="pa-select" id="country">
49
+ <option>United States</option>
50
+ <option>United Kingdom</option>
51
+ <option>Canada</option>
52
+ </select>
53
+ </div>
42
54
 
43
- <!-- INPUT SIZES -->
55
+ <div class="pa-form-actions">
56
+ <button type="reset" class="pa-btn pa-btn--secondary">Reset</button>
57
+ <button type="submit" class="pa-btn pa-btn--primary">Submit</button>
58
+ </div>
59
+ </form>
44
60
 
45
- <!-- Extra Small Input -->
46
- <input type="text" class="pa-input pa-input--xs" placeholder="Extra small">
47
61
 
48
- <!-- Small Input -->
62
+ <!-- INPUT / SELECT SIZES (shared --xs / --sm / default / --lg / --xl) -->
63
+
64
+ <input type="text" class="pa-input pa-input--xs" placeholder="Extra small">
49
65
  <input type="text" class="pa-input pa-input--sm" placeholder="Small">
66
+ <input type="text" class="pa-input" placeholder="Default">
67
+ <input type="text" class="pa-input pa-input--lg" placeholder="Large">
68
+ <input type="text" class="pa-input pa-input--xl" placeholder="Extra large">
50
69
 
51
- <!-- Default Input -->
52
- <input type="text" class="pa-input" placeholder="Default">
70
+ <select class="pa-select pa-select--sm"><option>Small select</option></select>
71
+ <select class="pa-select pa-select--lg"><option>Large select</option></select>
53
72
 
54
- <!-- Large Input -->
55
- <input type="text" class="pa-input pa-input--lg" placeholder="Large">
56
73
 
57
- <!-- Extra Large Input -->
58
- <input type="text" class="pa-input pa-input--xl" placeholder="Extra large">
74
+ <!-- TEXTAREA SIZES (sets height + min-height so it can grow but not collapse) -->
59
75
 
76
+ <textarea class="pa-textarea pa-textarea--xs" placeholder="xs (4rem)"></textarea>
77
+ <textarea class="pa-textarea pa-textarea--sm" placeholder="sm (6rem)"></textarea>
78
+ <textarea class="pa-textarea" placeholder="default (resize: vertical; $textarea-min-height)"></textarea>
79
+ <textarea class="pa-textarea pa-textarea--lg" placeholder="lg (12rem)"></textarea>
80
+ <textarea class="pa-textarea pa-textarea--xl" placeholder="xl (16rem)"></textarea>
60
81
 
61
- <!-- INPUT STATES -->
62
82
 
63
- <!-- Disabled Input -->
64
- <input type="text" class="pa-input" disabled value="Disabled input">
83
+ <!-- INPUT / TEXTAREA NATIVE STATES -->
65
84
 
66
- <!-- Readonly Input -->
85
+ <input type="text" class="pa-input" disabled value="Disabled input">
67
86
  <input type="text" class="pa-input" readonly value="Readonly input">
87
+ <input type="text" class="pa-input" required placeholder="Required (HTML5 attribute)">
68
88
 
69
- <!-- Required Input -->
70
- <div class="pa-form-group pa-form-group--required">
71
- <label class="pa-form-label" for="required-field">Required Field</label>
72
- <input type="text" class="pa-input" id="required-field" required>
73
- </div>
74
89
 
90
+ <!--
91
+ VALIDATION STATES
92
+ Two ways to apply a validation style — pick the one that fits your
93
+ framework wrapper:
94
+
95
+ 1. Standalone modifier on the control:
96
+ <input class="pa-input pa-input--error">
97
+
98
+ 2. Wrapper class on the form-group:
99
+ <div class="pa-form-group pa-form-group--error">
100
+ <input class="pa-input"> <!-- styled automatically -->
101
+ </div>
102
+
103
+ IMPORTANT: the form-group wrapper only paints .pa-input and .pa-select
104
+ borders — NOT .pa-textarea. There's also no .pa-textarea--error /
105
+ --success / --warning class. For textarea error visuals, lean on the
106
+ help text below (pa-form-help--error) and a red label — the border
107
+ just stays neutral.
108
+ -->
75
109
 
76
- <!-- VALIDATION STATES -->
77
-
78
- <!-- Success State -->
110
+ <!-- Success -->
79
111
  <div class="pa-form-group pa-form-group--success">
80
- <label class="pa-form-label" for="valid-input">Valid Input</label>
112
+ <label for="valid-input">Valid Input</label>
81
113
  <input type="text" class="pa-input" id="valid-input" value="Valid value">
82
114
  <small class="pa-form-help pa-form-help--success">Looks good!</small>
83
115
  </div>
84
116
 
85
- <!-- Warning State -->
117
+ <!-- Warning -->
86
118
  <div class="pa-form-group pa-form-group--warning">
87
- <label class="pa-form-label" for="warning-input">Warning Input</label>
119
+ <label for="warning-input">Warning Input</label>
88
120
  <input type="text" class="pa-input" id="warning-input" value="Check this">
89
121
  <small class="pa-form-help pa-form-help--warning">Please verify this field.</small>
90
122
  </div>
91
123
 
92
- <!-- Error State -->
124
+ <!-- Error -->
93
125
  <div class="pa-form-group pa-form-group--error">
94
- <label class="pa-form-label" for="invalid-input">Invalid Input</label>
126
+ <label for="invalid-input">Invalid Input</label>
95
127
  <input type="text" class="pa-input" id="invalid-input" value="Invalid value">
96
128
  <small class="pa-form-help pa-form-help--error">Please provide a valid value.</small>
97
129
  </div>
98
130
 
131
+ <!-- Standalone modifier (no form-group required) -->
132
+ <input type="text" class="pa-input pa-input--error" value="Inline error">
133
+ <select class="pa-select pa-select--warning"><option>Warning</option></select>
99
134
 
100
- <!-- THEME COLOR VARIANTS -->
101
- <!-- Use --pa-color-1 through --pa-color-9 CSS variables (set by theme) -->
102
135
 
103
- <!-- Color 1 with matching help text -->
104
- <div class="pa-form-group">
105
- <label class="pa-form-label" for="color1-input">Color 1</label>
106
- <input type="text" class="pa-input pa-input--color-1" id="color1-input" value="Color 1 input">
107
- <small class="pa-form-help pa-form-help--color-1">Colored help text</small>
108
- </div>
136
+ <!-- THEME COLOR SLOTS (1-9) -->
109
137
 
110
- <!-- Color 2 with matching help text -->
111
- <div class="pa-form-group">
112
- <label class="pa-form-label" for="color2-input">Color 2</label>
113
- <input type="text" class="pa-input pa-input--color-2" id="color2-input" value="Color 2 input">
114
- <small class="pa-form-help pa-form-help--color-2">Colored help text</small>
115
- </div>
138
+ <input type="text" class="pa-input pa-input--color-1" value="Theme colour 1">
139
+ <select class="pa-select pa-select--color-5"><option>Theme colour 5</option></select>
116
140
 
117
- <!-- Color 3 with gray help text (no color class) -->
118
- <div class="pa-form-group">
119
- <label class="pa-form-label" for="color3-input">Color 3</label>
120
- <input type="text" class="pa-input pa-input--color-3" id="color3-input" value="Color 3 input">
121
- <small class="pa-form-help">Gray help text (no color class)</small>
122
- </div>
141
+ <!-- Textarea DOES have colour variants (unlike validation states) -->
142
+ <textarea class="pa-textarea pa-textarea--color-2">Theme colour 2</textarea>
123
143
 
124
- <!-- Select with color variant -->
125
- <div class="pa-form-group">
126
- <label class="pa-form-label" for="color-select">Color Select</label>
127
- <select class="pa-select pa-select--color-1" id="color-select">
128
- <option>Option 1</option>
129
- <option>Option 2</option>
130
- </select>
131
- </div>
144
+ <!-- Help text colour variants -->
145
+ <small class="pa-form-help pa-form-help--color-3">Colour-3 help text</small>
146
+
147
+ <!-- Pattern: pa-input--color-{1..9}, pa-select--color-{1..9},
148
+ pa-textarea--color-{1..9}, pa-form-help--color-{1..9} -->
132
149
 
133
- <!-- Textarea with color variant -->
134
- <div class="pa-form-group">
135
- <label class="pa-form-label" for="color-textarea">Color Textarea</label>
136
- <textarea class="pa-textarea pa-textarea--color-2" id="color-textarea">Color 2 textarea</textarea>
137
- </div>
138
150
 
151
+ <!-- CUSTOM TRI-STATE CHECKBOX -->
139
152
 
140
- <!-- CUSTOM TRI-STATE CHECKBOXES -->
141
- <!-- States: unchecked, checked, indeterminate (set via JS: checkbox.indeterminate = true) -->
153
+ <!--
154
+ States: unchecked, checked, indeterminate. Set the indeterminate bit
155
+ via JS (the SCSS listens for `input:indeterminate`):
156
+
157
+ document.getElementById('my-cb').indeterminate = true;
158
+ -->
142
159
 
143
- <!-- Basic Checkbox -->
160
+ <!-- Basic unchecked -->
144
161
  <label class="pa-checkbox">
145
162
  <input type="checkbox">
146
163
  <span class="pa-checkbox__box"></span>
147
164
  <span class="pa-checkbox__label">Unchecked option</span>
148
165
  </label>
149
166
 
150
- <!-- Checked Checkbox -->
167
+ <!-- Checked -->
151
168
  <label class="pa-checkbox">
152
169
  <input type="checkbox" checked>
153
170
  <span class="pa-checkbox__box"></span>
154
171
  <span class="pa-checkbox__label">Checked option</span>
155
172
  </label>
156
173
 
157
- <!-- Indeterminate Checkbox (set via JavaScript) -->
174
+ <!-- Indeterminate third visual state; toggled by JS, not by the `indeterminate` HTML attribute -->
158
175
  <label class="pa-checkbox">
159
176
  <input type="checkbox" id="my-indeterminate-checkbox">
160
177
  <span class="pa-checkbox__box"></span>
@@ -162,90 +179,80 @@
162
179
  </label>
163
180
  <script>document.getElementById('my-indeterminate-checkbox').indeterminate = true;</script>
164
181
 
165
- <!-- Disabled Checkbox -->
182
+ <!-- Disabled either add --disabled, or just use input[disabled]: the
183
+ SCSS has a :has(input:disabled) rule so either works. -->
166
184
  <label class="pa-checkbox pa-checkbox--disabled">
167
185
  <input type="checkbox" disabled>
168
186
  <span class="pa-checkbox__box"></span>
169
187
  <span class="pa-checkbox__label">Disabled unchecked</span>
170
188
  </label>
171
189
 
172
- <label class="pa-checkbox pa-checkbox--disabled">
190
+ <label class="pa-checkbox">
173
191
  <input type="checkbox" checked disabled>
174
192
  <span class="pa-checkbox__box"></span>
175
- <span class="pa-checkbox__label">Disabled checked</span>
193
+ <span class="pa-checkbox__label">Disabled checked (auto-dimmed via :has)</span>
176
194
  </label>
177
195
 
178
- <!-- Checkbox Sizes -->
179
- <label class="pa-checkbox pa-checkbox--xs">
196
+ <!-- X-mark variant — swaps the checkmark for an X (useful for
197
+ "excluded" / "blocked" selections rather than yes/no). -->
198
+ <label class="pa-checkbox pa-checkbox--x">
180
199
  <input type="checkbox" checked>
181
200
  <span class="pa-checkbox__box"></span>
182
- <span class="pa-checkbox__label">Extra Small (xs)</span>
201
+ <span class="pa-checkbox__label">X-mark style when checked</span>
183
202
  </label>
184
203
 
204
+ <!-- Sizes (same scale as input/select) -->
205
+ <label class="pa-checkbox pa-checkbox--xs">
206
+ <input type="checkbox" checked>
207
+ <span class="pa-checkbox__box"></span>
208
+ <span class="pa-checkbox__label">Extra Small</span>
209
+ </label>
185
210
  <label class="pa-checkbox pa-checkbox--sm">
186
211
  <input type="checkbox" checked>
187
212
  <span class="pa-checkbox__box"></span>
188
- <span class="pa-checkbox__label">Small (sm)</span>
213
+ <span class="pa-checkbox__label">Small</span>
189
214
  </label>
190
-
191
215
  <label class="pa-checkbox">
192
216
  <input type="checkbox" checked>
193
217
  <span class="pa-checkbox__box"></span>
194
218
  <span class="pa-checkbox__label">Default</span>
195
219
  </label>
196
-
197
220
  <label class="pa-checkbox pa-checkbox--lg">
198
221
  <input type="checkbox" checked>
199
222
  <span class="pa-checkbox__box"></span>
200
- <span class="pa-checkbox__label">Large (lg)</span>
223
+ <span class="pa-checkbox__label">Large</span>
201
224
  </label>
202
-
203
225
  <label class="pa-checkbox pa-checkbox--xl">
204
226
  <input type="checkbox" checked>
205
227
  <span class="pa-checkbox__box"></span>
206
- <span class="pa-checkbox__label">Extra Large (xl)</span>
228
+ <span class="pa-checkbox__label">Extra Large</span>
207
229
  </label>
208
230
 
209
- <!-- Checkbox Group (vertical stack) -->
231
+ <!-- Vertical stack -->
210
232
  <div class="pa-checkbox-group">
211
- <label class="pa-checkbox">
212
- <input type="checkbox" checked>
213
- <span class="pa-checkbox__box"></span>
214
- <span class="pa-checkbox__label">Option A</span>
215
- </label>
216
- <label class="pa-checkbox">
217
- <input type="checkbox">
218
- <span class="pa-checkbox__box"></span>
219
- <span class="pa-checkbox__label">Option B</span>
220
- </label>
221
- <label class="pa-checkbox">
222
- <input type="checkbox">
223
- <span class="pa-checkbox__box"></span>
224
- <span class="pa-checkbox__label">Option C</span>
225
- </label>
233
+ <label class="pa-checkbox"><input type="checkbox" checked><span class="pa-checkbox__box"></span><span class="pa-checkbox__label">Option A</span></label>
234
+ <label class="pa-checkbox"><input type="checkbox"><span class="pa-checkbox__box"></span><span class="pa-checkbox__label">Option B</span></label>
235
+ <label class="pa-checkbox"><input type="checkbox"><span class="pa-checkbox__box"></span><span class="pa-checkbox__label">Option C</span></label>
226
236
  </div>
227
237
 
228
238
 
229
- <!-- RADIO BUTTONS (native styling) -->
239
+ <!-- RADIO BUTTONS (native styling + sizes) -->
230
240
 
231
- <!-- Radio Button Group -->
232
241
  <div class="pa-radio-group">
233
- <label class="pa-radio">
234
- <input type="radio" name="radioGroup" checked>
235
- Option 1 (selected)
236
- </label>
237
- <label class="pa-radio">
238
- <input type="radio" name="radioGroup">
239
- Option 2
240
- </label>
241
- <label class="pa-radio">
242
- <input type="radio" name="radioGroup" disabled>
243
- Option 3 (disabled)
244
- </label>
242
+ <label class="pa-radio"><input type="radio" name="radioGroup" checked> Option 1</label>
243
+ <label class="pa-radio"><input type="radio" name="radioGroup"> Option 2</label>
244
+ <label class="pa-radio"><input type="radio" name="radioGroup" disabled> Option 3 (disabled)</label>
245
245
  </div>
246
246
 
247
+ <!-- Radio sizes scale the native input (wrapper stays flex) -->
248
+ <label class="pa-radio pa-radio--xs"><input type="radio" name="sizeDemo"> xs</label>
249
+ <label class="pa-radio pa-radio--sm"><input type="radio" name="sizeDemo"> sm</label>
250
+ <label class="pa-radio"><input type="radio" name="sizeDemo"> default</label>
251
+ <label class="pa-radio pa-radio--lg"><input type="radio" name="sizeDemo"> lg</label>
252
+ <label class="pa-radio pa-radio--xl"><input type="radio" name="sizeDemo"> xl</label>
253
+
247
254
 
248
- <!-- INPUT GROUPS -->
255
+ <!-- INPUT GROUPS (prepend / append / button; RTL-aware) -->
249
256
 
250
257
  <!-- Prepend only -->
251
258
  <div class="pa-input-group">
@@ -259,74 +266,105 @@
259
266
  <span class="pa-input-group__append">USD</span>
260
267
  </div>
261
268
 
262
- <!-- Prepend and Append -->
269
+ <!-- Prepend + Append -->
263
270
  <div class="pa-input-group">
264
271
  <span class="pa-input-group__prepend">$</span>
265
272
  <input type="text" class="pa-input" placeholder="0.00">
266
273
  <span class="pa-input-group__append">.00</span>
267
274
  </div>
268
275
 
269
- <!-- Input with Button -->
276
+ <!-- Input with trailing button -->
270
277
  <div class="pa-input-group">
271
- <input type="text" class="pa-input" placeholder="Search...">
278
+ <input type="text" class="pa-input" placeholder="Search">
272
279
  <button class="pa-input-group__button pa-btn pa-btn--primary">Search</button>
273
280
  </div>
274
281
 
275
- <!-- Prepend + Input + Button -->
282
+ <!-- Multiple addons on one side (they get an internal divider) -->
276
283
  <div class="pa-input-group">
277
284
  <span class="pa-input-group__prepend">🔍</span>
278
- <input type="text" class="pa-input" placeholder="Search...">
285
+ <span class="pa-input-group__prepend">query</span>
286
+ <input type="text" class="pa-input" placeholder="Search…">
279
287
  <button class="pa-input-group__button pa-btn pa-btn--primary">Go</button>
280
288
  </div>
281
289
 
282
- <!-- Input + Append + Button -->
290
+ <!-- Quantity stepper -->
283
291
  <div class="pa-input-group">
284
- <input type="text" class="pa-input" placeholder="Enter amount">
285
- <span class="pa-input-group__append">USD</span>
286
- <button class="pa-input-group__button pa-btn pa-btn--success">Convert</button>
292
+ <button class="pa-input-group__button pa-btn pa-btn--secondary">−</button>
293
+ <input type="number" class="pa-input text-center" value="1">
294
+ <button class="pa-input-group__button pa-btn pa-btn--secondary">+</button>
287
295
  </div>
288
296
 
289
- <!-- Button + Input + Append -->
290
- <div class="pa-input-group">
291
- <button class="pa-input-group__button pa-btn pa-btn--secondary">-</button>
292
- <input type="number" class="pa-input" value="1">
293
- <span class="pa-input-group__append">items</span>
297
+ <!-- Input group sizes match prepend/append to the input size -->
298
+ <div class="pa-input-group pa-input-group--xs">
299
+ <span class="pa-input-group__prepend">@</span>
300
+ <input type="text" class="pa-input pa-input--xs" placeholder="xs">
294
301
  </div>
295
-
296
- <!-- Prepend + Input + Append + Button -->
297
- <div class="pa-input-group">
298
- <span class="pa-input-group__prepend">https://</span>
299
- <input type="text" class="pa-input" placeholder="example.com">
300
- <span class="pa-input-group__append">.com</span>
301
- <button class="pa-input-group__button pa-btn pa-btn--primary">Visit</button>
302
+ <div class="pa-input-group pa-input-group--sm">
303
+ <span class="pa-input-group__prepend">@</span>
304
+ <input type="text" class="pa-input pa-input--sm" placeholder="sm">
305
+ </div>
306
+ <div class="pa-input-group pa-input-group--lg">
307
+ <span class="pa-input-group__prepend">@</span>
308
+ <input type="text" class="pa-input pa-input--lg" placeholder="lg">
309
+ </div>
310
+ <div class="pa-input-group pa-input-group--xl">
311
+ <span class="pa-input-group__prepend">@</span>
312
+ <input type="text" class="pa-input pa-input--xl" placeholder="xl">
302
313
  </div>
303
314
 
304
- <!-- Button + Input + Button (quantity controls) -->
305
- <div class="pa-input-group">
306
- <button class="pa-input-group__button pa-btn pa-btn--secondary">-</button>
307
- <input type="number" class="pa-input text-center" value="1">
308
- <button class="pa-input-group__button pa-btn pa-btn--secondary">+</button>
315
+ <!-- Implicit: when .pa-input inside the group gets :focus, adjacent
316
+ prepend / append borders pick up the focus colour so the whole
317
+ group reads as focused. -->
318
+
319
+
320
+ <!-- INPUT WRAPPER (clear button) -->
321
+
322
+ <!--
323
+ .pa-input-wrapper positions a clear button over the trailing end of
324
+ any input-like element it contains. The input gets padding-inline-end
325
+ so text doesn't run under the button. Toggle .pa-input-wrapper__clear
326
+ --hidden via JS when the input is empty.
327
+ -->
328
+
329
+ <div class="pa-input-wrapper">
330
+ <input type="text" class="pa-input" id="searchInput" placeholder="Type to search…">
331
+ <button class="pa-input-wrapper__clear" type="button" aria-label="Clear"
332
+ onclick="document.getElementById('searchInput').value = '';">
333
+ ×
334
+ </button>
309
335
  </div>
310
336
 
311
337
 
312
- <!-- FORM LAYOUTS -->
338
+ <!-- SEARCH TOKENS (for query-builder pills inside an input-wrapper) -->
313
339
 
314
- <!-- Inline Form -->
315
- <form class="pa-form pa-form--inline">
316
- <input type="text" class="pa-input" placeholder="Username">
317
- <input type="password" class="pa-input" placeholder="Password">
318
- <button class="pa-btn pa-btn--primary">Sign in</button>
319
- </form>
340
+ <!--
341
+ A lightweight shell for rendering "tag"-style tokens inside a search
342
+ input. Pair with .pa-input-wrapper or .pa-virtual-textbox for interactive
343
+ builders. Each token is a pa-badge plus a close button.
344
+ -->
345
+
346
+ <div class="pa-input-wrapper">
347
+ <div class="pa-search-tokens">
348
+ <div class="pa-search-token-group">
349
+ <span class="pa-badge pa-badge--primary">status:active</span>
350
+ <button class="pa-search-token-remove" aria-label="Remove">×</button>
351
+ </div>
352
+ <div class="pa-search-token-group">
353
+ <span class="pa-badge pa-badge--info">role:admin</span>
354
+ <button class="pa-search-token-remove" aria-label="Remove">×</button>
355
+ </div>
356
+ </div>
357
+ <input type="text" class="pa-input" placeholder="Add filter…">
358
+ </div>
320
359
 
321
- <!-- Horizontal Form Groups (label left, input right) -->
322
360
 
323
- <!-- Single Horizontal Field -->
361
+ <!-- HORIZONTAL FORM GROUPS (label left, input right) -->
362
+
324
363
  <div class="pa-form-group pa-form-group--horizontal">
325
364
  <label for="email">Email</label>
326
365
  <input type="email" id="email" class="pa-input" placeholder="your@email.com">
327
366
  </div>
328
367
 
329
- <!-- Horizontal Field with Select -->
330
368
  <div class="pa-form-group pa-form-group--horizontal">
331
369
  <label for="department">Department</label>
332
370
  <select id="department" class="pa-select">
@@ -336,132 +374,69 @@
336
374
  </select>
337
375
  </div>
338
376
 
339
- <!-- Horizontal Field with Textarea -->
340
377
  <div class="pa-form-group pa-form-group--horizontal">
341
378
  <label for="notes">Notes</label>
342
- <textarea id="notes" class="pa-textarea" rows="3" placeholder="Additional notes..."></textarea>
379
+ <textarea id="notes" class="pa-textarea" rows="3" placeholder="Additional notes"></textarea>
343
380
  </div>
344
381
 
345
382
 
346
- <!-- HORIZONTAL FORM LAYOUTS WITH GRIDS -->
383
+ <!-- MULTI-COLUMN FORMS WITH THE GRID -->
347
384
 
348
- <!-- Multi-Column Horizontal Form (3 equal columns) -->
349
385
  <form class="pa-form">
350
386
  <div class="pa-row">
351
387
  <div class="pa-col-100 pa-col-md-1-3">
352
- <div class="pa-form-group pa-form-group--horizontal">
388
+ <div class="pa-form-group">
353
389
  <label for="fname">First Name</label>
354
390
  <input type="text" id="fname" class="pa-input" placeholder="John">
355
391
  </div>
356
392
  </div>
357
-
358
393
  <div class="pa-col-100 pa-col-md-1-3">
359
- <div class="pa-form-group pa-form-group--horizontal">
394
+ <div class="pa-form-group">
360
395
  <label for="lname">Last Name</label>
361
396
  <input type="text" id="lname" class="pa-input" placeholder="Doe">
362
397
  </div>
363
398
  </div>
364
-
365
399
  <div class="pa-col-100 pa-col-md-1-3">
366
- <div class="pa-form-group pa-form-group--horizontal">
367
- <label for="email">Email</label>
368
- <input type="email" id="email" class="pa-input" placeholder="john@example.com">
400
+ <div class="pa-form-group">
401
+ <label for="mail">Email</label>
402
+ <input type="email" id="mail" class="pa-input" placeholder="john@example.com">
369
403
  </div>
370
404
  </div>
371
405
  </div>
372
- </form>
373
-
374
- <!-- Multi-Column Horizontal Form (varying widths) -->
375
- <form class="pa-form">
376
- <div class="pa-row">
377
- <!-- Phone (smaller - 25%) -->
378
- <div class="pa-col-100 pa-col-md-25">
379
- <div class="pa-form-group pa-form-group--horizontal">
380
- <label for="phone">Phone</label>
381
- <input type="tel" id="phone" class="pa-input" placeholder="+1 555-0123">
382
- </div>
383
- </div>
384
406
 
385
- <!-- Department (larger - 50%) -->
386
- <div class="pa-col-100 pa-col-md-50">
387
- <div class="pa-form-group pa-form-group--horizontal">
388
- <label for="dept">Department</label>
389
- <select id="dept" class="pa-select">
390
- <option>Select...</option>
391
- <option>Engineering</option>
392
- <option>Marketing</option>
393
- </select>
394
- </div>
395
- </div>
396
-
397
- <!-- Job Title (medium - 25%) -->
398
- <div class="pa-col-100 pa-col-md-25">
399
- <div class="pa-form-group pa-form-group--horizontal">
400
- <label for="title">Job Title</label>
401
- <input type="text" id="title" class="pa-input" placeholder="Senior Developer">
402
- </div>
403
- </div>
407
+ <div class="pa-form-actions justify-content-end">
408
+ <button type="reset" class="pa-btn pa-btn--secondary">Reset</button>
409
+ <button type="submit" class="pa-btn pa-btn--primary">Submit</button>
404
410
  </div>
405
411
  </form>
406
412
 
407
413
 
408
- <!-- COMPLETE HORIZONTAL FORM EXAMPLE -->
414
+ <!-- COMPLETE FORM IN A CARD (with footer actions) -->
415
+
409
416
  <div class="pa-card">
410
- <div class="pa-card__header">
417
+ <div class="pa-card__header pa-card__header--underlined">
411
418
  <h3>Employee Information</h3>
412
419
  </div>
413
420
  <div class="pa-card__body">
414
421
  <form class="pa-form">
415
- <!-- Line 1: Personal Info -->
416
422
  <div class="pa-row">
417
- <div class="pa-col-100 pa-col-md-1-3">
418
- <div class="pa-form-group pa-form-group--horizontal">
423
+ <div class="pa-col-100 pa-col-md-1-2">
424
+ <div class="pa-form-group">
419
425
  <label for="first">First Name</label>
420
426
  <input type="text" id="first" class="pa-input" placeholder="John">
421
427
  </div>
422
428
  </div>
423
- <div class="pa-col-100 pa-col-md-1-3">
424
- <div class="pa-form-group pa-form-group--horizontal">
429
+ <div class="pa-col-100 pa-col-md-1-2">
430
+ <div class="pa-form-group">
425
431
  <label for="last">Last Name</label>
426
432
  <input type="text" id="last" class="pa-input" placeholder="Doe">
427
433
  </div>
428
434
  </div>
429
- <div class="pa-col-100 pa-col-md-1-3">
430
- <div class="pa-form-group pa-form-group--horizontal">
431
- <label for="mail">Email</label>
432
- <input type="email" id="mail" class="pa-input" placeholder="john.doe@company.com">
433
- </div>
434
- </div>
435
435
  </div>
436
436
 
437
- <!-- Line 2: Address -->
438
- <div class="pa-row">
439
- <div class="pa-col-100 pa-col-md-50">
440
- <div class="pa-form-group pa-form-group--horizontal">
441
- <label for="address">Address</label>
442
- <input type="text" id="address" class="pa-input" placeholder="123 Main Street">
443
- </div>
444
- </div>
445
- <div class="pa-col-100 pa-col-md-1-3">
446
- <div class="pa-form-group pa-form-group--horizontal">
447
- <label for="city">City</label>
448
- <input type="text" id="city" class="pa-input" placeholder="San Francisco">
449
- </div>
450
- </div>
451
- <div class="pa-col-100 pa-col-md-20">
452
- <div class="pa-form-group pa-form-group--horizontal">
453
- <label for="zip">Zip</label>
454
- <input type="text" id="zip" class="pa-input" placeholder="94102">
455
- </div>
456
- </div>
457
- </div>
458
-
459
- <!-- Submit Buttons -->
460
- <div class="pa-row">
461
- <div class="pa-col-100 text-end">
462
- <button type="button" class="pa-btn pa-btn--secondary">Cancel</button>
463
- <button type="submit" class="pa-btn pa-btn--primary">Submit</button>
464
- </div>
437
+ <div class="pa-form-actions justify-content-end">
438
+ <button type="button" class="pa-btn pa-btn--secondary">Cancel</button>
439
+ <button type="submit" class="pa-btn pa-btn--primary">Save</button>
465
440
  </div>
466
441
  </form>
467
442
  </div>
@@ -473,62 +448,187 @@
473
448
  ================================ -->
474
449
 
475
450
  <!--
476
- FORM ELEMENTS:
477
- - .pa-form: Form container
478
- - .pa-form--inline: Inline form layout
479
- - .pa-form-group: Form group wrapper
480
- - .pa-form-group--required: Shows required indicator
481
- - .pa-form-group--horizontal: Label-left, input-right layout
482
- - .pa-form-group--success: Success validation state
483
- - .pa-form-group--warning: Warning validation state
484
- - .pa-form-group--error: Error validation state
485
- - .pa-form-label: Form label
451
+ FORM CONTAINER:
452
+ - .pa-form Wraps the form; enables auto label/action
453
+ styling for direct .pa-form-group children.
454
+ - .pa-form-actions Button row (flex, gap, wrap). Inside .pa-form
455
+ it gets top margin so it sits below fields.
456
+ Combine with .justify-content-end (from
457
+ utilities) to right-align.
458
+
459
+ (No .pa-form--inline not defined in SCSS.)
460
+
461
+ FORM GROUP:
462
+ - .pa-form-group Wrapper around label + control + help text.
463
+ Auto margin reset when first/last child of
464
+ .pa-card__body (directly or via grid column).
465
+ - .pa-form-group--horizontal Label on the inline-start side, control fills
466
+ remaining space. Label gets padding-top to
467
+ align with the input's first line.
468
+
469
+ Validation wrappers (only affect .pa-input and .pa-select inside —
470
+ NOT .pa-textarea):
471
+ - .pa-form-group--success
472
+ - .pa-form-group--warning
473
+ - .pa-form-group--error
474
+
475
+ (No .pa-form-group--required class — use the native `required` attribute.)
476
+
477
+ LABELS:
478
+ Inside .pa-form .pa-form-group, a bare <label> gets framework styling
479
+ automatically (flex, gap, medium weight, $font-size-sm). There is NO
480
+ .pa-form-label class.
486
481
 
487
482
  INPUTS:
488
- - .pa-input: Text input base class
489
- - .pa-input--xs, --sm, --lg, --xl: Size variants
490
- - .pa-input--success, --warning, --error: Validation states
491
- - .pa-input--color-1 through --color-9: Theme color variants
492
-
493
- TEXTAREA:
494
- - .pa-textarea: Textarea element
495
- - .pa-textarea--xs, --sm, --lg, --xl: Size variants
496
- - .pa-textarea--success, --warning, --error: Validation states
497
- - .pa-textarea--color-1 through --color-9: Theme color variants
483
+ - .pa-input Base text input (width: 100%, md height)
484
+ - .pa-input--xs / --sm / --lg / --xl Size variants
485
+ - .pa-input--success / --warning / --error Standalone validation borders
486
+ - .pa-input--color-{1..9} Theme colour slot border
498
487
 
499
488
  SELECT:
500
- - .pa-select: Select dropdown
501
- - .pa-select--xs, --sm, --lg, --xl: Size variants
502
- - .pa-select--success, --warning, --error: Validation states
503
- - .pa-select--color-1 through --color-9: Theme color variants
504
-
505
- CHECKBOXES (custom tri-state):
506
- - .pa-checkbox: Checkbox wrapper label
507
- - .pa-checkbox__box: Custom checkbox visual
508
- - .pa-checkbox__label: Checkbox label text
509
- - .pa-checkbox--xs, --sm, --lg, --xl: Size variants
510
- - .pa-checkbox--disabled: Disabled state
511
- - .pa-checkbox-group: Vertical checkbox group
512
-
513
- RADIO BUTTONS:
514
- - .pa-radio: Radio wrapper label
515
- - .pa-radio-group: Vertical radio group
516
-
517
- INPUT GROUPS:
518
- - .pa-input-group: Input group container
519
- - .pa-input-group__prepend: Left addon
520
- - .pa-input-group__append: Right addon
521
- - .pa-input-group__button: Button addon
489
+ - .pa-select Base select
490
+ - .pa-select--xs / --sm / --lg / --xl
491
+ - .pa-select--success / --warning / --error
492
+ - .pa-select--color-{1..9}
493
+
494
+ TEXTAREA:
495
+ - .pa-textarea Base textarea (resize: vertical,
496
+ $textarea-min-height; size variants set
497
+ height AND min-height so it can grow but
498
+ not collapse).
499
+ - .pa-textarea--xs / --sm / --lg / --xl
500
+ - .pa-textarea--color-{1..9}
501
+ (NO --success / --warning / --error — textarea has no validation
502
+ border styling. Communicate textarea errors with pa-form-help--error
503
+ beneath the field.)
522
504
 
523
505
  HELP TEXT:
524
- - .pa-form-help: Help/hint text below inputs
525
- - .pa-form-help--success: Success message
526
- - .pa-form-help--warning: Warning message
527
- - .pa-form-help--error: Error message
528
- - .pa-form-help--color-1 through --color-9: Theme color variants
529
-
530
- GRID LAYOUT (for multi-column forms):
531
- - .pa-row: Row container (flexbox)
532
- - .pa-col-100: Full width on mobile
533
- - .pa-col-md-25, .pa-col-md-50, .pa-col-md-1-3, etc.: Responsive column widths
506
+ - .pa-form-help Small hint text below a control
507
+ - .pa-form-help--success
508
+ - .pa-form-help--warning
509
+ - .pa-form-help--error
510
+ - .pa-form-help--color-{1..9}
511
+
512
+ CHECKBOX (custom tri-state):
513
+ - .pa-checkbox <label> wrapper (contains the hidden native
514
+ input, the custom __box, and the __label).
515
+ - .pa-checkbox__box The drawn-in-CSS checkbox square.
516
+ - .pa-checkbox__label The visible label text.
517
+ - .pa-checkbox--xs / --sm / --lg / --xl Size variants (scale __box).
518
+ - .pa-checkbox--disabled Manual disabled style.
519
+ (Also auto-applied via :has(input:disabled),
520
+ so input[disabled] alone is enough.)
521
+ - .pa-checkbox--x Swap the checkmark for an X (for
522
+ "excluded" / "blocked" selection states).
523
+
524
+ States:
525
+ - input[checked] Filled square with tick
526
+ - input.indeterminate == true Filled square with dash (SET VIA JS —
527
+ the `indeterminate` attribute in HTML
528
+ has no effect; call
529
+ `el.indeterminate = true` instead)
530
+
531
+ - .pa-checkbox-group Vertical flex stack
532
+
533
+ RADIO:
534
+ - .pa-radio <label> wrapper for a native radio input
535
+ - .pa-radio--xs / --sm / --lg / --xl Size variants (scale the input)
536
+ - .pa-radio-group Vertical flex stack
537
+
538
+ INPUT GROUP (prepend / append / button — RTL-aware via logical borders):
539
+ - .pa-input-group
540
+ - .pa-input-group__prepend Inline-start addon
541
+ - .pa-input-group__append Inline-end addon
542
+ - .pa-input-group__button Button addon (keeps input's border radius
543
+ at the group ends, not on itself)
544
+ - .pa-input-group--xs / --sm / --lg / --xl Matches prepend/append height
545
+ to the input size
546
+
547
+ Implicit behaviour:
548
+ - When .pa-input inside the group is :focus, adjacent __prepend /
549
+ __append borders pick up the focus colour so the group reads as
550
+ focused together.
551
+ - Multiple __prepends (or __appends) in a row get internal dividers
552
+ between them.
553
+
554
+ INPUT WRAPPER (clear button):
555
+ - .pa-input-wrapper Relative flex container that positions
556
+ a trailing clear button over the input.
557
+ - .pa-input-wrapper__clear The close × button (absolute positioned,
558
+ inset-inline-end — RTL-aware).
559
+ - .pa-input-wrapper__clear--hidden Toggle via JS when input is empty.
560
+
561
+ SEARCH TOKENS (lightweight query-builder pills):
562
+ - .pa-search-tokens Flex-wrap container for tokens.
563
+ - .pa-search-token-group One token = pa-badge + close button.
564
+ - .pa-search-token-remove The × inside a token group.
565
+
566
+ SMART FILTERS / QUERY EDITOR
567
+ (SCSS file: forms/_query-editor.scss; demo page: /tables/smart-filters,
568
+ wired up by search-autocomplete.js + search-autocomplete-v2.js +
569
+ virtual-textbox.js. Separate family — full coverage pending a dedicated
570
+ snippet — probably smart-filters.html):
571
+ - .pa-search-highlight + __overlay / __input / __field / __operator
572
+ - .pa-search-autocomplete + __item / __item-name / __item-type /
573
+ __section / __empty / __item--active
574
+ - .pa-virtual-textbox Contenteditable input with inline badges.
575
+ Uses data-placeholder attribute for empty
576
+ state.
577
+ - .pa-inline-query-editor + __layers / __highlights / __input
578
+ - .pa-inline-query-token + --field / --operator / --value / --keyword
579
+ + --invalid (wavy red underline)
580
+ - .pa-inline-query-autocomplete + rich __item substructure
581
+
582
+ IMPLICIT BEHAVIOURS:
583
+ - .pa-form-group margin resets to 0 when it's the first/last child of a
584
+ .pa-card__body (directly or through pure-g / grid column wrappers).
585
+ - Dark-mode webkit date picker icons get filter: invert(1) for visibility.
586
+ - All input/select/textarea get a focus ring using --pa-accent-light.
587
+
588
+ STRUCTURE PATTERNS:
589
+
590
+ Basic form group:
591
+ <div class="pa-form-group">
592
+ <label for="x">Label</label>
593
+ <input class="pa-input" id="x">
594
+ </div>
595
+
596
+ With validation + help text:
597
+ <div class="pa-form-group pa-form-group--error">
598
+ <label for="x">Email</label>
599
+ <input class="pa-input" id="x" value="not-an-email">
600
+ <small class="pa-form-help pa-form-help--error">Enter a valid email.</small>
601
+ </div>
602
+
603
+ Input with prepend/append and button:
604
+ <div class="pa-input-group">
605
+ <span class="pa-input-group__prepend">@</span>
606
+ <input class="pa-input" type="text">
607
+ <button class="pa-input-group__button pa-btn pa-btn--primary">Go</button>
608
+ </div>
609
+
610
+ Tri-state checkbox (indeterminate set via JS):
611
+ <label class="pa-checkbox">
612
+ <input type="checkbox" id="parent">
613
+ <span class="pa-checkbox__box"></span>
614
+ <span class="pa-checkbox__label">Select all</span>
615
+ </label>
616
+ <script>
617
+ document.getElementById('parent').indeterminate = true;
618
+ </script>
619
+
620
+ Multi-column form with grid:
621
+ <form class="pa-form">
622
+ <div class="pa-row">
623
+ <div class="pa-col-100 pa-col-md-1-2">
624
+ <div class="pa-form-group">…</div>
625
+ </div>
626
+ <div class="pa-col-100 pa-col-md-1-2">
627
+ <div class="pa-form-group">…</div>
628
+ </div>
629
+ </div>
630
+ <div class="pa-form-actions justify-content-end">
631
+ <button type="submit" class="pa-btn pa-btn--primary">Submit</button>
632
+ </div>
633
+ </form>
534
634
  -->