@threadlabs/looma 0.39.2 → 0.39.4
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/ui-combobox/ui-combobox.html +8 -3
- package/components/ui-combobox/ui-combobox.js +3 -2
- package/components/ui-dialog/ui-dialog.html +6 -52
- package/components/ui-input/ui-input.html +21 -3
- package/components/ui-input-group/ui-input-group.html +1 -0
- package/components/ui-select/ui-select.html +10 -3
- package/dist/index.js +1 -1
- package/docs/README.md +1 -1
- package/docs/catalog.json +1 -25
- package/docs/customizer.js +11 -3
- package/docs/site.mjs +9 -5
- package/docs/theme.mjs +8 -2
- package/package.json +1 -1
- package/styles/ui-combobox.css +16 -6
- package/styles/ui-dialog.css +6 -107
- package/styles/ui-input-group.css +1 -0
- package/styles/ui-input.css +24 -3
- package/styles/ui-select.css +10 -3
- package/theme-system.css +4 -1
- package/vanilla/UiDialog.js +2 -2
- package/vue/UiCombobox.vue +22 -6
- package/vue/UiDialog.d.ts +3 -3
- package/vue/UiDialog.vue +18 -111
- package/vue/UiInput.vue +30 -3
- package/vue/UiInputGroup.vue +1 -0
- package/vue/UiSelect.vue +16 -3
- package/vue/chunks/UiCombobox.js +1 -1
- package/vue/chunks/UiDialog.js +22 -14
- package/vue/chunks/UiInput.js +1 -1
- package/vue/chunks/UiInputGroup.js +1 -1
- package/vue/chunks/UiSelect.js +1 -1
- package/vue/components.css +105 -118
package/docs/README.md
CHANGED
|
@@ -256,7 +256,7 @@ Use the `@threadlabs/looma/docs` entrypoint for programmatic discovery, catalog
|
|
|
256
256
|
|
|
257
257
|
## Saving and recovery
|
|
258
258
|
|
|
259
|
-
The local editor writes only the configured recipe and CSS inside the project. It rejects symlink paths, generated or dependency directories, cross-origin save requests and stale file revisions. A competing edit leaves your unsaved preview in place; reload and reconcile it before saving again. Examples run in isolated frames and receive theme CSS, never the local editing capability.
|
|
259
|
+
The local editor writes only the configured recipe and CSS inside the project. It rejects symlink paths, generated or dependency directories, cross-origin save requests and stale file revisions. A competing edit leaves your unsaved preview in place; reload and reconcile it before saving again. Examples run in isolated frames and receive theme CSS, never the local editing capability. Each preview's compiled runtime is embedded after its content and authorized by an exact content hash in the generated content policy. This keeps previews interactive in Safari while retaining their opaque origin and parent-page isolation.
|
|
260
260
|
|
|
261
261
|
The writer stages complete files, serializes saves with a project lock, and rolls back the recipe if replacing the CSS fails. If a process stops during a save or build, close the old editor before removing its `.looma/theme-write.lock` or `.looma/docs-build.lock`; restart and save the loaded recipe to regenerate a consistent pair. Change the server base, output directory or theme-file paths by restarting `dev`.
|
|
262
262
|
|
package/docs/catalog.json
CHANGED
|
@@ -458,7 +458,7 @@
|
|
|
458
458
|
],
|
|
459
459
|
"schemaVersion": 1,
|
|
460
460
|
"package": "@threadlabs/looma",
|
|
461
|
-
"version": "0.39.
|
|
461
|
+
"version": "0.39.4",
|
|
462
462
|
"groups": [
|
|
463
463
|
{
|
|
464
464
|
"id": "layout",
|
|
@@ -7430,24 +7430,12 @@
|
|
|
7430
7430
|
{
|
|
7431
7431
|
"name": "--ui-border-width"
|
|
7432
7432
|
},
|
|
7433
|
-
{
|
|
7434
|
-
"name": "--ui-control-min-block-size"
|
|
7435
|
-
},
|
|
7436
|
-
{
|
|
7437
|
-
"name": "--ui-control-size-sm"
|
|
7438
|
-
},
|
|
7439
7433
|
{
|
|
7440
7434
|
"name": "--ui-elevation-dialog",
|
|
7441
7435
|
"fallbacks": [
|
|
7442
7436
|
"var(--ui-shadow-xl)"
|
|
7443
7437
|
]
|
|
7444
7438
|
},
|
|
7445
|
-
{
|
|
7446
|
-
"name": "--ui-focus-ring"
|
|
7447
|
-
},
|
|
7448
|
-
{
|
|
7449
|
-
"name": "--ui-focus-width"
|
|
7450
|
-
},
|
|
7451
7439
|
{
|
|
7452
7440
|
"name": "--ui-font-semibold"
|
|
7453
7441
|
},
|
|
@@ -7493,9 +7481,6 @@
|
|
|
7493
7481
|
{
|
|
7494
7482
|
"name": "--ui-motion-ease"
|
|
7495
7483
|
},
|
|
7496
|
-
{
|
|
7497
|
-
"name": "--ui-motion-fast"
|
|
7498
|
-
},
|
|
7499
7484
|
{
|
|
7500
7485
|
"name": "--ui-overlay-backdrop"
|
|
7501
7486
|
},
|
|
@@ -7527,9 +7512,6 @@
|
|
|
7527
7512
|
{
|
|
7528
7513
|
"name": "--ui-radius-dialog"
|
|
7529
7514
|
},
|
|
7530
|
-
{
|
|
7531
|
-
"name": "--ui-radius-round"
|
|
7532
|
-
},
|
|
7533
7515
|
{
|
|
7534
7516
|
"name": "--ui-shadow-xl"
|
|
7535
7517
|
},
|
|
@@ -7557,9 +7539,6 @@
|
|
|
7557
7539
|
"linear-gradient(transparent, transparent)"
|
|
7558
7540
|
]
|
|
7559
7541
|
},
|
|
7560
|
-
{
|
|
7561
|
-
"name": "--ui-surface-muted"
|
|
7562
|
-
},
|
|
7563
7542
|
{
|
|
7564
7543
|
"name": "--ui-surface-separation",
|
|
7565
7544
|
"fallbacks": [
|
|
@@ -7568,9 +7547,6 @@
|
|
|
7568
7547
|
},
|
|
7569
7548
|
{
|
|
7570
7549
|
"name": "--ui-text-primary"
|
|
7571
|
-
},
|
|
7572
|
-
{
|
|
7573
|
-
"name": "--ui-text-secondary"
|
|
7574
7550
|
}
|
|
7575
7551
|
]
|
|
7576
7552
|
},
|
package/docs/customizer.js
CHANGED
|
@@ -66,11 +66,19 @@ export function connect(host) {
|
|
|
66
66
|
};
|
|
67
67
|
const header = document.querySelector('[data-component~="markupress-header"]');
|
|
68
68
|
const previews = [...root.querySelectorAll('[data-editor-preview], .palette-preview')];
|
|
69
|
+
let layoutFrame = 0;
|
|
69
70
|
const updatePreviewLayout = () => {
|
|
70
71
|
const chrome = header?.getBoundingClientRect().height ?? 0;
|
|
71
72
|
root.style.setProperty('--_editor-chrome-height', `${chrome}px`);
|
|
72
73
|
for (const preview of previews) preview.toggleAttribute('data-page-flow', preview.offsetHeight > (view.innerHeight - chrome) / 2);
|
|
73
74
|
};
|
|
75
|
+
// Layout writes must run outside ResizeObserver delivery, including when a preview appears.
|
|
76
|
+
const schedulePreviewLayout = () => {
|
|
77
|
+
if (!layoutFrame) layoutFrame = view.requestAnimationFrame(() => {
|
|
78
|
+
layoutFrame = 0;
|
|
79
|
+
updatePreviewLayout();
|
|
80
|
+
});
|
|
81
|
+
};
|
|
74
82
|
const replayMotion = () => {
|
|
75
83
|
const element = root.querySelector('[data-motion-object]');
|
|
76
84
|
const duration = parseFloat(convertValueUnit(recipe.values['response-time'], 'ms', unitContext()));
|
|
@@ -405,9 +413,9 @@ export function connect(host) {
|
|
|
405
413
|
filterExamples();
|
|
406
414
|
};
|
|
407
415
|
const motionPreferenceChanged = () => { motionAnimation?.cancel(); paint(); };
|
|
408
|
-
const previewObserver = new view.ResizeObserver(
|
|
416
|
+
const previewObserver = new view.ResizeObserver(schedulePreviewLayout);
|
|
409
417
|
for (const element of [...previews, ...(header ? [header] : [])]) previewObserver.observe(element);
|
|
410
|
-
view.addEventListener('resize',
|
|
418
|
+
view.addEventListener('resize', schedulePreviewLayout);
|
|
411
419
|
motionPreference.addEventListener('change', motionPreferenceChanged);
|
|
412
420
|
root.addEventListener('toggle', updateSizes, true);
|
|
413
421
|
root.addEventListener('looma-example-remove', remove);
|
|
@@ -438,5 +446,5 @@ export function connect(host) {
|
|
|
438
446
|
root.querySelector('[data-action="save"]').hidden = false;
|
|
439
447
|
}).catch(error => { if (alive && error.name !== 'AbortError') say(error.message, true); });
|
|
440
448
|
}
|
|
441
|
-
return () => { previewObserver.disconnect(); view.removeEventListener('resize',
|
|
449
|
+
return () => { previewObserver.disconnect(); if (layoutFrame) view.cancelAnimationFrame(layoutFrame); view.removeEventListener('resize', schedulePreviewLayout); motionPreference.removeEventListener('change', motionPreferenceChanged); root.removeEventListener('toggle', updateSizes, true); motionAnimation?.cancel(); flush(); root.removeEventListener('cancel', cancel); root.removeEventListener('keydown', keyboard); alive = false; abort.abort(); root.removeEventListener('looma-example-remove', remove); root.removeEventListener('input', input); root.removeEventListener('value-change', choose); root.removeEventListener('value-change', selectExample); root.removeEventListener('value-change', selectFont); root.removeEventListener('blur', blur, true); root.removeEventListener('change', change); root.removeEventListener('click', click); view.removeEventListener('beforeunload', flush); view.removeEventListener('message', ready); view.removeEventListener('popstate', renderView); view.removeEventListener('hashchange', renderView); view.removeEventListener(themeEvent, synchronize); };
|
|
442
450
|
}
|
package/docs/site.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cp, mkdir, readFile, readdir, realpath, rename, rm, symlink, unlink, writeFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
4
4
|
import { createRequire } from 'node:module';
|
|
5
5
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
6
|
import { discoverComponents, escapeHtml, mergeCatalogs, readCatalog, readJson } from './catalog.mjs';
|
|
@@ -220,7 +220,7 @@ export async function buildDocumentation({ root = process.cwd(), config = {}, ca
|
|
|
220
220
|
});
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
async function isolatePreviewScripts(site, { outDir
|
|
223
|
+
async function isolatePreviewScripts(site, { outDir }) {
|
|
224
224
|
const { build } = await tool('vite', site.root);
|
|
225
225
|
// Preview entries and outputs are independent. Keep compiler memory bounded
|
|
226
226
|
// and retain the build lock until every in-flight bundle has settled.
|
|
@@ -236,9 +236,13 @@ async function isolatePreviewScripts(site, { outDir, writeFile: writeOutput }) {
|
|
|
236
236
|
const input = path.join(outDir, url.slice(site.base.length));
|
|
237
237
|
const bundle = await build({ configFile: false, logLevel: 'silent', build: { write: false, minify: true, lib: { entry: input, name: 'LoomaPreview', formats: ['iife'] }, rollupOptions: { output: { codeSplitting: false } } } });
|
|
238
238
|
const code = (Array.isArray(bundle) ? bundle[0] : bundle).output.find(output => output.type === 'chunk').code;
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
239
|
+
// A sandbox has an opaque origin: Safari cannot authorize this runtime via 'self'.
|
|
240
|
+
// Hash the exact inline bytes and run after the preview DOM, preserving isolation.
|
|
241
|
+
const script = code.replace(/<\/script/gi, '<\\/script');
|
|
242
|
+
const hash = createHash('sha256').update(script).digest('base64');
|
|
243
|
+
if (!/<meta\b[^>]*http-equiv="Content-Security-Policy"[^>]*content="[^"]*\bscript-src\b/.test(html)) throw new Error(`Missing script policy for isolated preview ${example.id}.`);
|
|
244
|
+
html = html.replace(/(<meta\b[^>]*http-equiv="Content-Security-Policy"[^>]*content="[^"]*\bscript-src)\b/, `$1 'sha256-${hash}'`);
|
|
245
|
+
html = html.replace(tag, '').replace('</body>', () => `<script>${script}</script></body>`);
|
|
242
246
|
}
|
|
243
247
|
html = html.replace(/<link\b[^>]*rel="modulepreload"[^>]*>/g, '');
|
|
244
248
|
await writeFile(htmlFile, html);
|
package/docs/theme.mjs
CHANGED
|
@@ -273,15 +273,21 @@ export function themeCSS(input, systemCSS = '', { scope = 'portable' } = {}) {
|
|
|
273
273
|
const selector = scope === 'documentation'
|
|
274
274
|
? ':root[data-looma-documentation-theme], :root[data-looma-documentation-theme] [data-theme], :root[data-looma-documentation-theme] [data-contrast]'
|
|
275
275
|
: ':root, [data-theme], [data-contrast]';
|
|
276
|
-
const
|
|
276
|
+
const recipe = validateRecipe(input);
|
|
277
|
+
const fontURL = googleFontURL(recipe.fonts);
|
|
277
278
|
const imports = fontURL ? `@import url("${fontURL}");\n` : '';
|
|
278
279
|
const light = deriveTheme(input, { mode: 'light' }).tokens;
|
|
279
280
|
const dark = deriveTheme(input, { mode: 'dark' }).tokens;
|
|
280
281
|
const palette = [], geometry = [];
|
|
282
|
+
const relativeLengths = new Map(themeFields.filter(field => field.property && field.type.includes('length') && recipe.values[field.id].endsWith('rem')).map(field => [field.property, recipe.values[field.id]]));
|
|
281
283
|
for (const [name, value] of Object.entries(light)) {
|
|
282
284
|
if (name === 'color-scheme') continue;
|
|
283
285
|
if (/^#[\da-f]{6}$/i.test(value)) palette.push(` ${name}: light-dark(${value}, ${dark[name]});`);
|
|
284
|
-
else
|
|
286
|
+
else {
|
|
287
|
+
const relative = relativeLengths.get(name);
|
|
288
|
+
const resolved = relative ? value.replace(relative, `calc(${parseFloat(relative)} * var(--_ui-root-font-unit, 1rem))`) : value;
|
|
289
|
+
geometry.push(` ${name}: ${resolved};`);
|
|
290
|
+
}
|
|
285
291
|
}
|
|
286
292
|
const geometrySelector = scope === 'documentation' ? ':root[data-looma-documentation-theme]' : ':root';
|
|
287
293
|
const chevron = color => `url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='${color.replace('#', '%23')}' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E")`;
|
package/package.json
CHANGED
package/styles/ui-combobox.css
CHANGED
|
@@ -198,10 +198,12 @@
|
|
|
198
198
|
color: inherit;
|
|
199
199
|
}
|
|
200
200
|
input {
|
|
201
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-md, var(--ui-space-2));
|
|
201
202
|
appearance: none;
|
|
202
203
|
width: 100%;
|
|
203
204
|
min-inline-size: 0;
|
|
204
205
|
min-block-size: calc(var(--_row) - 2 * var(--ui-border-width));
|
|
206
|
+
max-block-size: max(calc(var(--_row) - 2 * var(--ui-border-width)), calc(1em * var(--ui-line-height-tight) + 2 * var(--_ui-combobox-inset-block)));
|
|
205
207
|
box-sizing: border-box !important;
|
|
206
208
|
border: 0;
|
|
207
209
|
border-radius: inherit;
|
|
@@ -209,7 +211,7 @@
|
|
|
209
211
|
color: inherit;
|
|
210
212
|
font: inherit;
|
|
211
213
|
line-height: var(--ui-line-height-tight);
|
|
212
|
-
padding: var(--
|
|
214
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-3) !important;
|
|
213
215
|
margin: 0 !important;
|
|
214
216
|
outline: none;
|
|
215
217
|
}
|
|
@@ -217,14 +219,16 @@
|
|
|
217
219
|
--_row: var(--ui-control-size-sm);
|
|
218
220
|
}
|
|
219
221
|
:scope:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=sm"] input {
|
|
220
|
-
|
|
222
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-sm, var(--ui-space-1));
|
|
223
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-2) !important;
|
|
221
224
|
font-size: var(--ui-font-size-sm);
|
|
222
225
|
}
|
|
223
226
|
:scope:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=lg"] {
|
|
224
227
|
--_row: var(--ui-control-size-lg);
|
|
225
228
|
}
|
|
226
229
|
:scope:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=lg"] input {
|
|
227
|
-
|
|
230
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-lg, var(--ui-space-3));
|
|
231
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-4) !important;
|
|
228
232
|
font-size: var(--ui-font-size-md);
|
|
229
233
|
}
|
|
230
234
|
button {
|
|
@@ -359,6 +363,7 @@
|
|
|
359
363
|
display: none !important;
|
|
360
364
|
}
|
|
361
365
|
:scope:where([data-component~="ui-combobox"])[data-ui-combobox-state~="multiple"] input {
|
|
366
|
+
max-block-size: none;
|
|
362
367
|
flex: 1 0 var(--_ui-combobox-item-input-min-inline-size, 5rem);
|
|
363
368
|
width: auto;
|
|
364
369
|
min-block-size: 1.75rem;
|
|
@@ -617,10 +622,12 @@
|
|
|
617
622
|
color: inherit;
|
|
618
623
|
}
|
|
619
624
|
input:where(:scope, :scope *) {
|
|
625
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-md, var(--ui-space-2));
|
|
620
626
|
appearance: none;
|
|
621
627
|
width: 100%;
|
|
622
628
|
min-inline-size: 0;
|
|
623
629
|
min-block-size: calc(var(--_row) - 2 * var(--ui-border-width));
|
|
630
|
+
max-block-size: max(calc(var(--_row) - 2 * var(--ui-border-width)), calc(1em * var(--ui-line-height-tight) + 2 * var(--_ui-combobox-inset-block)));
|
|
624
631
|
box-sizing: border-box !important;
|
|
625
632
|
border: 0;
|
|
626
633
|
border-radius: inherit;
|
|
@@ -628,7 +635,7 @@
|
|
|
628
635
|
color: inherit;
|
|
629
636
|
font: inherit;
|
|
630
637
|
line-height: var(--ui-line-height-tight);
|
|
631
|
-
padding: var(--
|
|
638
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-3) !important;
|
|
632
639
|
margin: 0 !important;
|
|
633
640
|
outline: none;
|
|
634
641
|
}
|
|
@@ -636,14 +643,16 @@
|
|
|
636
643
|
--_row: var(--ui-control-size-sm);
|
|
637
644
|
}
|
|
638
645
|
:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=sm"] input:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
|
|
639
|
-
|
|
646
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-sm, var(--ui-space-1));
|
|
647
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-2) !important;
|
|
640
648
|
font-size: var(--ui-font-size-sm);
|
|
641
649
|
}
|
|
642
650
|
:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=lg"]:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
|
|
643
651
|
--_row: var(--ui-control-size-lg);
|
|
644
652
|
}
|
|
645
653
|
:where([data-component~="ui-combobox"])[data-ui-combobox-state~="size=lg"] input:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
|
|
646
|
-
|
|
654
|
+
--_ui-combobox-inset-block: var(--ui-control-inset-block-lg, var(--ui-space-3));
|
|
655
|
+
padding: var(--_ui-combobox-inset-block) var(--ui-space-4) !important;
|
|
647
656
|
font-size: var(--ui-font-size-md);
|
|
648
657
|
}
|
|
649
658
|
button:where(:scope, :scope *) {
|
|
@@ -776,6 +785,7 @@
|
|
|
776
785
|
display: none !important;
|
|
777
786
|
}
|
|
778
787
|
:where([data-component~="ui-combobox"])[data-ui-combobox-state~="multiple"] input:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-combobox"]))) {
|
|
788
|
+
max-block-size: none;
|
|
779
789
|
flex: 1 0 var(--_ui-combobox-item-input-min-inline-size, 5rem);
|
|
780
790
|
width: auto;
|
|
781
791
|
min-block-size: 1.75rem;
|
package/styles/ui-dialog.css
CHANGED
|
@@ -193,58 +193,10 @@
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
.close {
|
|
196
|
-
display:
|
|
197
|
-
|
|
198
|
-
flex: none;
|
|
196
|
+
display: flex !important;
|
|
197
|
+
align-items: center !important;
|
|
198
|
+
flex: none !important;
|
|
199
199
|
margin-inline-start: auto !important;
|
|
200
|
-
inline-size: var(--ui-control-size-sm);
|
|
201
|
-
block-size: var(--ui-control-size-sm);
|
|
202
|
-
padding: 0 !important;
|
|
203
|
-
border: 0;
|
|
204
|
-
border-radius: var(--ui-radius-round);
|
|
205
|
-
background: transparent;
|
|
206
|
-
color: var(--ui-text-secondary);
|
|
207
|
-
cursor: pointer;
|
|
208
|
-
transition: background-color var(--ui-motion-fast) var(--ui-motion-ease), color var(--ui-motion-fast) var(--ui-motion-ease);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/* A CSS-drawn X. */
|
|
212
|
-
.close:where(:not([data-component], [data-slotted]))::before,
|
|
213
|
-
.close:where(:not([data-component], [data-slotted]))::after {
|
|
214
|
-
content: "";
|
|
215
|
-
grid-area: 1 / 1;
|
|
216
|
-
inline-size: 0.875rem;
|
|
217
|
-
block-size: 1.5px;
|
|
218
|
-
border-radius: 1px;
|
|
219
|
-
background: currentColor;
|
|
220
|
-
transform: rotate(45deg);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
.close:where(:not([data-component], [data-slotted]))::after {
|
|
224
|
-
transform: rotate(-45deg);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
.close:hover {
|
|
228
|
-
background: var(--ui-surface-muted);
|
|
229
|
-
color: var(--ui-text-primary);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
.close:focus-visible {
|
|
233
|
-
outline: var(--ui-focus-width) solid var(--ui-focus-ring);
|
|
234
|
-
outline-offset: var(--ui-focus-width);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
/* A touch target, like Button on coarse pointers. */
|
|
238
|
-
@media (pointer: coarse) {
|
|
239
|
-
.close {
|
|
240
|
-
inline-size: var(--ui-control-min-block-size);
|
|
241
|
-
block-size: var(--ui-control-min-block-size);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
html[data-ui-input-modality="touch"] :scope:where([data-component~="ui-dialog"]) .close {
|
|
246
|
-
inline-size: var(--ui-control-min-block-size);
|
|
247
|
-
block-size: var(--ui-control-min-block-size);
|
|
248
200
|
}
|
|
249
201
|
|
|
250
202
|
.body {
|
|
@@ -320,12 +272,6 @@
|
|
|
320
272
|
/* Alerts have no close target to add height above the title. */
|
|
321
273
|
|
|
322
274
|
/* :host-qualified so page heading rules that reach in cannot outrank the dialog chrome. */
|
|
323
|
-
|
|
324
|
-
/* A CSS-drawn X. */
|
|
325
|
-
|
|
326
|
-
/* A touch target, like Button on coarse pointers. */
|
|
327
|
-
@media (pointer: coarse) {
|
|
328
|
-
}
|
|
329
275
|
}
|
|
330
276
|
@scope ([data-slotted~="ui-dialog"]:not([data-component])) to ([data-component], [data-slotted]) {
|
|
331
277
|
/* This component's hooks style the element they are set on, and never a nested one. */
|
|
@@ -514,57 +460,10 @@
|
|
|
514
460
|
}
|
|
515
461
|
|
|
516
462
|
.close:where(:scope, :scope *) {
|
|
517
|
-
display:
|
|
518
|
-
|
|
519
|
-
flex: none;
|
|
463
|
+
display: flex !important;
|
|
464
|
+
align-items: center !important;
|
|
465
|
+
flex: none !important;
|
|
520
466
|
margin-inline-start: auto !important;
|
|
521
|
-
inline-size: var(--ui-control-size-sm);
|
|
522
|
-
block-size: var(--ui-control-size-sm);
|
|
523
|
-
padding: 0 !important;
|
|
524
|
-
border: 0;
|
|
525
|
-
border-radius: var(--ui-radius-round);
|
|
526
|
-
background: transparent;
|
|
527
|
-
color: var(--ui-text-secondary);
|
|
528
|
-
cursor: pointer;
|
|
529
|
-
transition: background-color var(--ui-motion-fast) var(--ui-motion-ease), color var(--ui-motion-fast) var(--ui-motion-ease);
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
/* A CSS-drawn X. */
|
|
533
|
-
.close:where(:not([data-component], [data-slotted]:not([data-slotted~="ui-dialog"]))):where(:scope, :scope *)::before, .close:where(:not([data-component], [data-slotted]:not([data-slotted~="ui-dialog"]))):where(:scope, :scope *)::after {
|
|
534
|
-
content: "";
|
|
535
|
-
grid-area: 1 / 1;
|
|
536
|
-
inline-size: 0.875rem;
|
|
537
|
-
block-size: 1.5px;
|
|
538
|
-
border-radius: 1px;
|
|
539
|
-
background: currentColor;
|
|
540
|
-
transform: rotate(45deg);
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
.close:where(:not([data-component], [data-slotted]:not([data-slotted~="ui-dialog"]))):where(:scope, :scope *)::after {
|
|
544
|
-
transform: rotate(-45deg);
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
.close:hover:where(:scope, :scope *) {
|
|
548
|
-
background: var(--ui-surface-muted);
|
|
549
|
-
color: var(--ui-text-primary);
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
.close:focus-visible:where(:scope, :scope *) {
|
|
553
|
-
outline: var(--ui-focus-width) solid var(--ui-focus-ring);
|
|
554
|
-
outline-offset: var(--ui-focus-width);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
/* A touch target, like Button on coarse pointers. */
|
|
558
|
-
@media (pointer: coarse) {
|
|
559
|
-
.close:where(:scope, :scope *) {
|
|
560
|
-
inline-size: var(--ui-control-min-block-size);
|
|
561
|
-
block-size: var(--ui-control-min-block-size);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
html[data-ui-input-modality="touch"] :where([data-component~="ui-dialog"]) .close:where(:scope, :not([data-component], [data-slotted]:not([data-slotted~="ui-dialog"]))) {
|
|
566
|
-
inline-size: var(--ui-control-min-block-size);
|
|
567
|
-
block-size: var(--ui-control-min-block-size);
|
|
568
467
|
}
|
|
569
468
|
|
|
570
469
|
.body:where(:scope, :scope *) {
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
Hooks do not inherit, so these are defaults for the input; its own hooks still win. */
|
|
33
33
|
--_ui-default-input-border-width: 0;
|
|
34
34
|
--_ui-default-input-min-block-size: 0;
|
|
35
|
+
--_ui-default-input-frame-width: calc(2 * var(--ui-border-width));
|
|
35
36
|
--_ui-default-input-border: transparent;
|
|
36
37
|
--_ui-default-input-border-hover: transparent;
|
|
37
38
|
--_ui-default-input-focus-border: transparent;
|
package/styles/ui-input.css
CHANGED
|
@@ -61,15 +61,24 @@
|
|
|
61
61
|
syntax: "*";
|
|
62
62
|
inherits: false;
|
|
63
63
|
}
|
|
64
|
+
/* Normalize a unitless zero border to a length before the box calculation. */
|
|
65
|
+
@property --_ui-input-stroke {
|
|
66
|
+
syntax: "<length>";
|
|
67
|
+
inherits: false;
|
|
68
|
+
initial-value: 1px;
|
|
69
|
+
}
|
|
64
70
|
|
|
65
71
|
:scope:where([data-component~="ui-input"]) {
|
|
72
|
+
--_ui-input-size: var(--ui-control-size-md);
|
|
73
|
+
--_ui-input-inset-block: var(--ui-control-inset-block-md, var(--ui-space-2));
|
|
74
|
+
--_ui-input-stroke: var(--ui-input-border-width, var(--_ui-default-input-border-width, var(--ui-border-width)));
|
|
66
75
|
box-sizing: border-box !important;
|
|
67
76
|
display: inline-block !important;
|
|
68
77
|
inline-size: var(--ui-input-inline-size, 100%);
|
|
69
78
|
min-inline-size: 0;
|
|
70
79
|
min-block-size: var(--ui-input-min-block-size, var(--_ui-default-input-min-block-size, var(--ui-control-size-md)));
|
|
71
80
|
margin: 0 !important;
|
|
72
|
-
padding: var(--
|
|
81
|
+
padding: var(--_ui-input-inset-block) var(--ui-space-3) !important;
|
|
73
82
|
appearance: none;
|
|
74
83
|
padding-inline-start: var(--_ui-default-input-padding-inline-start, var(--ui-space-3)) !important;
|
|
75
84
|
border: var(--ui-input-border-width, var(--_ui-default-input-border-width, var(--ui-border-width))) solid
|
|
@@ -87,16 +96,25 @@
|
|
|
87
96
|
background-color var(--ui-motion-fast) var(--ui-motion-ease);
|
|
88
97
|
}
|
|
89
98
|
|
|
99
|
+
/* Cap Safari's excess text metrics without changing native date, file, color or range widgets. */
|
|
100
|
+
:scope:where([data-component~="ui-input"]):is([type="text"], [type="search"], [type="tel"], [type="url"], [type="email"], [type="password"], [type="number"]) {
|
|
101
|
+
max-block-size: max(calc(var(--_ui-input-size) - var(--_ui-default-input-frame-width, 0px)), calc(1em * var(--ui-line-height-tight) + 2 * var(--_ui-input-inset-block) + 2 * var(--_ui-input-stroke)));
|
|
102
|
+
}
|
|
103
|
+
|
|
90
104
|
/* Each size reads the hook and then an Input Group's default first, so a group, which draws the frame, can zero it. */
|
|
91
105
|
:scope:where([data-component~="ui-input"])[data-ui-input-state~="size=sm"] {
|
|
106
|
+
--_ui-input-size: var(--ui-control-size-sm);
|
|
107
|
+
--_ui-input-inset-block: var(--ui-control-inset-block-sm, var(--ui-space-1));
|
|
92
108
|
min-block-size: var(--ui-input-min-block-size, var(--_ui-default-input-min-block-size, var(--ui-control-size-sm)));
|
|
93
|
-
padding: var(--
|
|
109
|
+
padding: var(--_ui-input-inset-block) var(--ui-space-2) !important;
|
|
94
110
|
font-size: var(--ui-font-size-sm);
|
|
95
111
|
}
|
|
96
112
|
|
|
97
113
|
:scope:where([data-component~="ui-input"])[data-ui-input-state~="size=lg"] {
|
|
114
|
+
--_ui-input-size: var(--ui-control-size-lg);
|
|
115
|
+
--_ui-input-inset-block: var(--ui-control-inset-block-lg, var(--ui-space-3));
|
|
98
116
|
min-block-size: var(--ui-input-min-block-size, var(--_ui-default-input-min-block-size, var(--ui-control-size-lg)));
|
|
99
|
-
padding: var(--
|
|
117
|
+
padding: var(--_ui-input-inset-block) var(--ui-space-4) !important;
|
|
100
118
|
font-size: var(--ui-font-size-md);
|
|
101
119
|
}
|
|
102
120
|
|
|
@@ -177,6 +195,9 @@
|
|
|
177
195
|
@scope ([data-component~="ui-input"]) to ([data-component]) {
|
|
178
196
|
/* This component's hooks style the element they are set on, and never a nested one. An Input
|
|
179
197
|
Group sets --_ui-default-input-* for the input inside it, below the input's own hooks. */
|
|
198
|
+
/* Normalize a unitless zero border to a length before the box calculation. */
|
|
199
|
+
|
|
200
|
+
/* Cap Safari's excess text metrics without changing native date, file, color or range widgets. */
|
|
180
201
|
|
|
181
202
|
/* Each size reads the hook and then an Input Group's default first, so a group, which draws the frame, can zero it. */
|
|
182
203
|
|
package/styles/ui-select.css
CHANGED
|
@@ -50,13 +50,16 @@
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
:scope:where([data-component~="ui-select"]) {
|
|
53
|
+
--_ui-select-size: var(--ui-control-size-md);
|
|
54
|
+
--_ui-select-inset-block: var(--ui-control-inset-block-md, var(--ui-space-2));
|
|
53
55
|
box-sizing: border-box !important;
|
|
54
56
|
display: inline-block !important;
|
|
55
57
|
inline-size: var(--ui-select-inline-size, 100%);
|
|
56
58
|
min-inline-size: 0;
|
|
59
|
+
max-block-size: max(var(--_ui-select-size), calc(1em * var(--ui-line-height-tight) + 2 * var(--_ui-select-inset-block) + 2 * var(--ui-border-width)));
|
|
57
60
|
min-block-size: var(--ui-control-size-md);
|
|
58
61
|
margin: 0 !important;
|
|
59
|
-
padding: var(--
|
|
62
|
+
padding: var(--_ui-select-inset-block) var(--ui-space-3) !important;
|
|
60
63
|
border: var(--ui-border-width) solid var(--ui-select-border, var(--ui-control-border, var(--ui-border-strong)));
|
|
61
64
|
border-radius: var(--ui-select-radius, var(--ui-field-radius, var(--ui-radius-md)));
|
|
62
65
|
background-color: var(--ui-select-surface, var(--ui-control-surface, var(--ui-surface-elevated)));
|
|
@@ -129,8 +132,10 @@
|
|
|
129
132
|
}
|
|
130
133
|
|
|
131
134
|
:scope:where([data-component~="ui-select"])[data-ui-select-state~="size=sm"] {
|
|
135
|
+
--_ui-select-size: var(--ui-control-size-sm);
|
|
136
|
+
--_ui-select-inset-block: var(--ui-control-inset-block-sm, var(--ui-space-1));
|
|
132
137
|
min-block-size: var(--ui-control-size-sm);
|
|
133
|
-
padding: var(--
|
|
138
|
+
padding: var(--_ui-select-inset-block) var(--ui-space-2) !important;
|
|
134
139
|
padding-inline-end: calc(var(--ui-space-2) + var(--ui-icon-size-sm) + var(--ui-space-1)) !important;
|
|
135
140
|
background-position: right var(--ui-space-2) center, center;
|
|
136
141
|
background-size:
|
|
@@ -140,8 +145,10 @@
|
|
|
140
145
|
}
|
|
141
146
|
|
|
142
147
|
:scope:where([data-component~="ui-select"])[data-ui-select-state~="size=lg"] {
|
|
148
|
+
--_ui-select-size: var(--ui-control-size-lg);
|
|
149
|
+
--_ui-select-inset-block: var(--ui-control-inset-block-lg, var(--ui-space-3));
|
|
143
150
|
min-block-size: var(--ui-control-size-lg);
|
|
144
|
-
padding: var(--
|
|
151
|
+
padding: var(--_ui-select-inset-block) var(--ui-space-4) !important;
|
|
145
152
|
padding-inline-end: calc(var(--ui-space-4) + 1.5rem) !important;
|
|
146
153
|
background-position: right var(--ui-space-4) center, center;
|
|
147
154
|
font-size: var(--ui-font-size-md);
|
package/theme-system.css
CHANGED
|
@@ -8,11 +8,14 @@
|
|
|
8
8
|
/* Keep the root font seed unregistered: a registered rem length used by root
|
|
9
9
|
font-size creates a dependency cycle (CSS Properties and Values API 2.7.2). */
|
|
10
10
|
@property --ui-control-size { syntax: "<length>"; inherits: true; initial-value: 40px; }
|
|
11
|
+
@property --_ui-root-font-unit { syntax: "<length>"; inherits: true; initial-value: 16px; }
|
|
11
12
|
@property --ui-radius-md { syntax: "<length>"; inherits: true; initial-value: 8px; }
|
|
12
13
|
@property --ui-border-width { syntax: "<length>"; inherits: true; initial-value: 1px; }
|
|
13
14
|
|
|
14
15
|
@layer theme {
|
|
15
16
|
:root {
|
|
17
|
+
/* Resolve once on the root: WebKit can resolve registered rem lengths against the initial font size. */
|
|
18
|
+
--_ui-root-font-unit: 1em;
|
|
16
19
|
--ui-spacing: 1;
|
|
17
20
|
--ui-rhythm: 1;
|
|
18
21
|
--ui-control-roominess: 2.5;
|
|
@@ -35,7 +38,7 @@
|
|
|
35
38
|
:where(:root, [data-theme], [data-contrast]) {
|
|
36
39
|
/* Controls use a ratio. Spacing uses an independent curve, so increasing the size
|
|
37
40
|
contrast cannot unexpectedly turn linear spacing into quadratic spacing. */
|
|
38
|
-
--ui-control-size: calc(
|
|
41
|
+
--ui-control-size: calc(var(--_ui-root-font-unit) * var(--ui-control-roominess));
|
|
39
42
|
--_ui-theme-ratio: max(1, var(--ui-scale-ratio));
|
|
40
43
|
--_ui-theme-unit: calc(1rem * 0.25 * max(0, var(--ui-spacing)) * clamp(0.75, var(--ui-rhythm), 1.5));
|
|
41
44
|
--ui-line-height-tight: clamp(1.1, calc(1.2 + (var(--ui-rhythm) - 1) * 0.2), 1.3);
|
package/vanilla/UiDialog.js
CHANGED
|
@@ -3,7 +3,7 @@ import { attachGeneratedController, buildTemplate, clearRegion, toAttribute, toT
|
|
|
3
3
|
import * as controller from "../components/ui-dialog/ui-dialog.js";
|
|
4
4
|
import "../styles/ui-dialog.css";
|
|
5
5
|
|
|
6
|
-
const T0 = ["",[],"\n ",["header",[],"\n ",1,"\n ",["
|
|
6
|
+
const T0 = ["",[],"\n ",["header",[],"\n ",1,"\n ",["div",["class","close"],["ui-icon-button",["size","sm","round","","label","Close"],["ui-icon",["name","x"]]]],"\n "],"\n ",["div",["class","body"],["ui-scroll-area",[],4]],"\n ",["footer",[],4],"\n "];
|
|
7
7
|
const T1 = ["h2",["class","title"],0];
|
|
8
8
|
const T2 = ["",[]];
|
|
9
9
|
const T3 = ["",[]];
|
|
@@ -82,7 +82,7 @@ export function createUiDialog(options = {}, html) {
|
|
|
82
82
|
};
|
|
83
83
|
const t0 = element.firstChild.nextSibling;
|
|
84
84
|
const t1 = t0.firstChild.nextSibling;
|
|
85
|
-
const t2 = t1.nextSibling.nextSibling.nextSibling;
|
|
85
|
+
const t2 = t1.nextSibling.nextSibling.nextSibling.firstChild;
|
|
86
86
|
const t3 = t0.nextSibling.nextSibling;
|
|
87
87
|
const t4 = t3.firstChild;
|
|
88
88
|
const t5 = t4.firstChild;
|