@webority/theme 0.6.9 → 0.6.11

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webority/theme",
3
- "version": "0.6.9",
3
+ "version": "0.6.11",
4
4
  "description": "Webority shared design system — Bootstrap 5.3 SCSS overrides, --wui-* tokens, and shared component CSS. The single source of brand chrome for React portals and Razor sites.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "publishConfig": {
@@ -201,6 +201,24 @@
201
201
  min-height: var(--wui-control-height-lg);
202
202
  font-size: 16px;
203
203
  }
204
+ // Multi-select grows with its chips, so a size sets the FLOOR (min-height from the rule
205
+ // above) but never a fixed height — reset height:auto so a second row of chips can appear.
206
+ .search-select-sm .search-select-multi,
207
+ .search-select-lg .search-select-multi {
208
+ height: auto;
209
+ }
210
+ // AppSelectMini is <wui-select compact> (a 32px picker). Its sizes are compact-scaled —
211
+ // the compound selector outranks both the plain size rule and the compact rule.
212
+ .search-select-compact.search-select-sm .search-select-control {
213
+ height: 28px;
214
+ min-height: 28px;
215
+ font-size: 12px;
216
+ }
217
+ .search-select-compact.search-select-lg .search-select-control {
218
+ height: 38px;
219
+ min-height: 38px;
220
+ font-size: 14px;
221
+ }
204
222
 
205
223
  // Clear (✕) — sits between the value and the caret, shown only once a value is
206
224
  // chosen. It is a <span role="button"> inside the trigger <button> (a nested
@@ -227,6 +245,35 @@
227
245
  color: var(--color-text);
228
246
  }
229
247
 
248
+ // Leading icon + static prefix (e.g. a funnel glyph, or "Sort:") — cluster at the
249
+ // start of the control, before the value. Mainly for the mini toolbar picker.
250
+ .search-select-icon {
251
+ flex-shrink: 0;
252
+ display: inline-flex;
253
+ color: var(--color-text-muted);
254
+ font-size: 15px;
255
+ }
256
+ .search-select-prefix {
257
+ flex-shrink: 0;
258
+ color: var(--color-text-subtle);
259
+ font-weight: 400;
260
+ }
261
+
262
+ // Per-option leading glyph — wraps icon + label as one node (menu row, the selected
263
+ // value in the control, and chips all render through optionLabelHtml). The icon inherits
264
+ // the row's colour so it recolours with the label on hover / when selected.
265
+ .wui-opt-label {
266
+ display: inline-flex;
267
+ align-items: center;
268
+ gap: 8px;
269
+ min-width: 0;
270
+ vertical-align: middle;
271
+ }
272
+ .wui-opt-label > .bi {
273
+ flex-shrink: 0;
274
+ font-size: 15px;
275
+ }
276
+
230
277
  // AppSelectMini — the compact single-purpose picker (page size, a range switch). It shares
231
278
  // every behaviour with AppSelect and differs only in metrics.
232
279
  //
@@ -172,6 +172,134 @@
172
172
  vertical-align: middle;
173
173
  }
174
174
 
175
+ // ── Premium checkbox / radio / switch ──
176
+ // AppCheckbox / AppRadio / AppSwitch render Bootstrap's .form-check markup, whose default
177
+ // input is a plain 16px box. This restyles it to the Webority selection-control language
178
+ // (see .lx-check above): a 20px squircle box, brand fill, a crisp white tick, a soft primary
179
+ // hover ring + focus ring, and a themed switch. One block upgrades all three, both surfaces.
180
+ .form-check {
181
+ display: flex;
182
+ align-items: center;
183
+ gap: 8px;
184
+ min-height: 0;
185
+ margin-bottom: 0;
186
+ padding-left: 0;
187
+ }
188
+ .form-check-inline {
189
+ display: inline-flex;
190
+ margin-right: 16px;
191
+ }
192
+ .form-check-input {
193
+ box-sizing: border-box;
194
+ flex: none;
195
+ float: none;
196
+ width: 20px;
197
+ height: 20px;
198
+ margin: 0;
199
+ appearance: none;
200
+ -webkit-appearance: none;
201
+ border: 1.6px solid var(--color-border-strong);
202
+ background-color: var(--color-surface);
203
+ background-repeat: no-repeat;
204
+ background-position: center;
205
+ background-size: 15px 15px;
206
+ cursor: pointer;
207
+ transition:
208
+ border-color 0.15s ease,
209
+ background-color 0.15s ease,
210
+ box-shadow 0.15s ease,
211
+ background-position 0.15s ease;
212
+ }
213
+ // Bootstrap floats the input into the label's left padding via `.form-check .form-check-input`
214
+ // (specificity 0,2,0 — it outranks the rule above), which drags the box a negative margin
215
+ // left, out of the card. Our flex row places it directly; undo the float and negative margin
216
+ // at matching specificity.
217
+ .form-check .form-check-input {
218
+ float: none;
219
+ margin: 0;
220
+ }
221
+ .form-check-input[type="checkbox"] {
222
+ border-radius: var(--radius-sm);
223
+ corner-shape: squircle;
224
+ }
225
+ .form-check-input[type="radio"] {
226
+ border-radius: 50%;
227
+ }
228
+ .form-check-input:hover:not(:disabled):not(:checked) {
229
+ border-color: var(--color-primary);
230
+ box-shadow: 0 0 0 4px var(--color-primary-soft);
231
+ }
232
+ .form-check-input:focus-visible {
233
+ outline: none;
234
+ border-color: var(--color-primary);
235
+ box-shadow: 0 0 0 3px var(--color-primary-soft);
236
+ }
237
+ .form-check-input:checked {
238
+ border-color: var(--color-primary);
239
+ background-color: var(--color-primary);
240
+ }
241
+ .form-check-input[type="checkbox"]:checked {
242
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7'/%3E%3C/svg%3E");
243
+ }
244
+ .form-check-input[type="checkbox"]:indeterminate {
245
+ border-color: var(--color-primary);
246
+ background-color: var(--color-primary);
247
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.4' stroke-linecap='round'%3E%3Cpath d='M6 12h12'/%3E%3C/svg%3E");
248
+ }
249
+ .form-check-input[type="radio"]:checked {
250
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='5.5' fill='white'/%3E%3C/svg%3E");
251
+ background-size: 20px 20px;
252
+ }
253
+ .form-check-input:disabled {
254
+ cursor: not-allowed;
255
+ opacity: 0.55;
256
+ background-color: var(--color-bg-muted);
257
+ border-color: var(--color-border);
258
+ box-shadow: none;
259
+ }
260
+ .form-check-label {
261
+ cursor: pointer;
262
+ font-size: 14px;
263
+ line-height: 1.4;
264
+ color: var(--color-text);
265
+ user-select: none;
266
+ }
267
+ .form-check-input:disabled ~ .form-check-label {
268
+ cursor: not-allowed;
269
+ color: var(--color-text-subtle);
270
+ }
271
+ // Switch — a pill track with a sliding white knob.
272
+ .form-switch {
273
+ padding-left: 0;
274
+ }
275
+ .form-switch .form-check-input {
276
+ width: 40px;
277
+ height: 22px;
278
+ border-radius: 999px;
279
+ border-color: transparent;
280
+ background-color: var(--color-border-strong);
281
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Ccircle cx='10' cy='10' r='7' fill='white'/%3E%3C/svg%3E");
282
+ background-position: left center;
283
+ background-size: 18px 18px;
284
+ }
285
+ .form-switch .form-check-input:hover:not(:disabled):not(:checked) {
286
+ border-color: transparent;
287
+ box-shadow: 0 0 0 4px var(--color-primary-soft);
288
+ }
289
+ .form-switch .form-check-input:checked {
290
+ border-color: transparent;
291
+ background-color: var(--color-primary);
292
+ background-position: right center;
293
+ // Re-assert the knob — the checkbox :checked tick has equal specificity and would
294
+ // otherwise win, turning the switch into a ticked box.
295
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Ccircle cx='10' cy='10' r='7' fill='white'/%3E%3C/svg%3E");
296
+ }
297
+ @media (prefers-reduced-motion: reduce) {
298
+ .form-check-input {
299
+ transition: none;
300
+ }
301
+ }
302
+
175
303
  // ── Field / Input ──
176
304
  // Inputs — always-white fill, 16px squircle, soft brand border tint on focus
177
305
  // (no glow).
@@ -268,17 +268,86 @@
268
268
  background: transparent;
269
269
  color: var(--color-text-subtle);
270
270
  cursor: pointer;
271
+ // `visibility`, not opacity alone: an opacity-0 button keeps its place in the tab order,
272
+ // so an empty field handed a keyboard user an invisible "Clear search" that cleared
273
+ // nothing. Delayed on the way out so the fade still plays, immediate on the way in.
271
274
  opacity: 0;
275
+ visibility: hidden;
272
276
  pointer-events: none;
273
277
  transition:
274
278
  opacity 0.12s ease,
279
+ visibility 0s linear 0.12s,
275
280
  color 0.12s ease;
276
281
  }
277
- .search-wrap.has-value .search-clear,
278
- .search-wrap:focus-within .search-clear {
282
+ // Revealed by CONTENT only. Revealing on :focus-within as well put a ✕ on an empty Razor
283
+ // field while React — which renders the button only when there is a value — showed none:
284
+ // the same component disagreeing across surfaces, which is the one thing this library exists
285
+ // to prevent.
286
+ .search-wrap.has-value .search-clear {
279
287
  opacity: 1;
288
+ visibility: visible;
280
289
  pointer-events: auto;
290
+ transition:
291
+ opacity 0.12s ease,
292
+ visibility 0s,
293
+ color 0.12s ease;
281
294
  }
282
295
  .search-clear:hover {
283
296
  color: var(--color-text);
284
297
  }
298
+ // type=search draws the browser's own cancel ✕; the component ships its own .search-clear,
299
+ // so the native one would double up. Remove it (WebKit/Blink).
300
+ .search-input::-webkit-search-cancel-button,
301
+ .search-input::-webkit-search-decoration {
302
+ -webkit-appearance: none;
303
+ appearance: none;
304
+ display: none;
305
+ }
306
+ // Sizes — sm / lg around the default, mirroring AppInput. The leading icon and clear button
307
+ // track the height so they stay centred and clear of the text.
308
+ .search-wrap.search-sm .search-input {
309
+ height: var(--wui-control-height-sm);
310
+ padding-left: 34px;
311
+ padding-right: 32px;
312
+ font-size: 13px;
313
+ }
314
+ .search-wrap.search-sm > .search-icon,
315
+ .search-wrap.search-sm .search-spinner {
316
+ left: 11px;
317
+ }
318
+ .search-wrap.search-sm .search-clear {
319
+ width: 22px;
320
+ height: 22px;
321
+ right: 6px;
322
+ }
323
+ .search-wrap.search-lg .search-input {
324
+ height: var(--wui-control-height-lg);
325
+ padding-left: 46px;
326
+ padding-right: 40px;
327
+ font-size: 16px;
328
+ }
329
+ .search-wrap.search-lg > .search-icon,
330
+ .search-wrap.search-lg .search-spinner {
331
+ left: 16px;
332
+ }
333
+ // Loading — a spinner replaces the leading glyph while a search is in flight.
334
+ .search-wrap.is-loading > .search-icon {
335
+ display: none;
336
+ }
337
+ .search-spinner {
338
+ position: absolute;
339
+ left: 13px;
340
+ top: 50%;
341
+ margin-top: -9px;
342
+ width: 18px;
343
+ height: 18px;
344
+ border: 2px solid var(--color-border-strong);
345
+ border-top-color: var(--color-primary);
346
+ border-radius: 50%;
347
+ animation: lx-spin 0.7s linear infinite;
348
+ pointer-events: none;
349
+ }
350
+ .search-input:disabled {
351
+ cursor: not-allowed;
352
+ opacity: 0.6;
353
+ }
@@ -221,7 +221,7 @@
221
221
 
222
222
  /* ── Multi-select (chips in the control; check-list dropdown; extends .search-select) ── */
223
223
  .search-select-multi {
224
- align-items: flex-start;
224
+ align-items: center;
225
225
  gap: 6px;
226
226
  min-height: 40px;
227
227
  height: auto;
@@ -237,7 +237,21 @@
237
237
  min-width: 0;
238
238
  align-items: center;
239
239
  }
240
- .search-select-multi .search-select-caret {
240
+ // Collapse mode (collapse-chips): chips stay on one row; the JS folds the overflow into
241
+ // a "+N" counter (see WuiMultiSelect._collapseChips).
242
+ wui-multiselect[collapse-chips] .ms-chips {
243
+ flex-wrap: nowrap;
244
+ overflow: hidden;
245
+ }
246
+ .chip-more {
247
+ flex-shrink: 0;
248
+ background: var(--color-primary-soft);
249
+ border-color: transparent;
250
+ color: var(--color-primary);
251
+ font-weight: 600;
252
+ }
253
+ .search-select-multi .search-select-caret,
254
+ .search-select-multi .search-select-icon {
241
255
  align-self: center;
242
256
  }
243
257
  .ms-check {
@@ -253,9 +267,11 @@
253
267
  opacity: 1;
254
268
  }
255
269
  .ms-opt-label {
270
+ flex: 1;
256
271
  min-width: 0;
257
272
  overflow: hidden;
258
273
  text-overflow: ellipsis;
274
+ text-align: left;
259
275
  }
260
276
  .search-select-multi .search-select-option {
261
277
  display: flex;