@adia-ai/web-components 0.7.10 → 0.7.12
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/CHANGELOG.md +34 -2
- package/README.md +2 -2
- package/USAGE.md +2 -2
- package/components/button/button.css +43 -16
- package/components/frame/frame.a2ui.json +1 -1
- package/components/frame/frame.css +9 -6
- package/components/frame/frame.d.ts +5 -3
- package/components/frame/frame.yaml +7 -5
- package/components/list/list.css +4 -0
- package/components/preview/preview.class.js +9 -0
- package/components/theme-provider/theme-provider.a2ui.json +88 -0
- package/components/theme-provider/theme-provider.class.js +134 -0
- package/components/theme-provider/theme-provider.css +18 -0
- package/components/theme-provider/theme-provider.d.ts +45 -0
- package/components/theme-provider/theme-provider.js +22 -0
- package/components/theme-provider/theme-provider.test.js +94 -0
- package/components/theme-provider/theme-provider.yaml +96 -0
- package/dist/host.min.css +1 -1
- package/dist/host.sheet.js +11 -0
- package/dist/prose.min.css +1 -0
- package/dist/prose.sheet.js +11 -0
- package/dist/themes.min.css +1 -0
- package/dist/themes.sheet.js +11 -0
- package/dist/verse.min.css +1 -0
- package/dist/verse.sheet.js +11 -0
- package/dist/web-components.min.css +1 -1
- package/dist/web-components.min.js +1 -1
- package/dist/web-components.sheet.js +11 -0
- package/package.json +5 -1
- package/styles/README.md +1 -1
- package/styles/colors/parameters.css +1 -1
- package/styles/colors/primitives-accent.css +1 -1
- package/styles/colors/primitives-brand.css +1 -1
- package/styles/colors/primitives-danger.css +1 -1
- package/styles/colors/primitives-info.css +1 -1
- package/styles/colors/primitives-neutral.css +1 -1
- package/styles/colors/primitives-success.css +1 -1
- package/styles/colors/primitives-warning.css +1 -1
- package/styles/colors/scrims.css +1 -1
- package/styles/colors/semantics/aliases.css +1 -1
- package/styles/colors/semantics/buckets.css +1 -1
- package/styles/colors/semantics/core.css +1 -1
- package/styles/colors/semantics/data-viz.css +1 -1
- package/styles/colors/semantics/features.css +1 -1
- package/styles/colors/surfaces.css +1 -1
- package/styles/components.css +1 -0
- package/styles/design-tokens-export.js +1 -1
- package/styles/host.css +1 -1
- package/styles/prose.css +1 -1
- package/styles/themes.css +12 -12
- package/styles/verse.css +1 -1
- package/traits/resizable/resizable.js +49 -36
- package/traits/resizable/resizable.test.js +19 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adia-ai/web-components",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.12",
|
|
4
4
|
"description": "AdiaUI web components \u2014 vanilla custom elements. A2UI runtime (renderer, registry, streams, wiring) lives in @adia-ai/a2ui-runtime.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./index.d.ts",
|
|
@@ -21,6 +21,10 @@
|
|
|
21
21
|
"types": "./css-module.d.ts",
|
|
22
22
|
"default": "./dist/web-components.min.css"
|
|
23
23
|
},
|
|
24
|
+
"./css/sheet": {
|
|
25
|
+
"import": "./dist/web-components.sheet.js",
|
|
26
|
+
"default": "./dist/web-components.sheet.js"
|
|
27
|
+
},
|
|
24
28
|
"./js/bundled": {
|
|
25
29
|
"types": "./index.d.ts",
|
|
26
30
|
"import": "./dist/web-components.min.js",
|
package/styles/README.md
CHANGED
|
@@ -296,7 +296,7 @@ styles/
|
|
|
296
296
|
│ ├── text.css # [text-align] [weight] [color] [transform] [truncate]
|
|
297
297
|
│ └── layout.css # [hidden] [nomargin] [grow]
|
|
298
298
|
│
|
|
299
|
-
├── themes.css # CONTEXT: 8 named themes + light/dark scheme overrides ([
|
|
299
|
+
├── themes.css # CONTEXT: 8 named themes + light/dark scheme overrides ([theme])
|
|
300
300
|
├── prose.css # CONTEXT: opt-in article typography ([prose])
|
|
301
301
|
├── resets.css # global RESET (box-sizing, scrollbar, focus, normalize)
|
|
302
302
|
├── fonts.css # @font-face declarations + font-family primitives
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
Using @property for typed, animatable custom properties.
|
|
5
5
|
syntax: "<number>" enables CSS transitions/animations on hue,
|
|
6
6
|
chroma, and lightness parameters. inherits: true allows
|
|
7
|
-
theme-ui and [
|
|
7
|
+
theme-ui and [theme] to override per-subtree.
|
|
8
8
|
═══════════════════════════════════════════════════════════════ */
|
|
9
9
|
|
|
10
10
|
/* ── System bounds ── */
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Requires primitives-shared.css and parameters.css.
|
|
6
6
|
═══════════════════════════════════════════════════════════════ */
|
|
7
7
|
|
|
8
|
-
:root, theme-ui, [
|
|
8
|
+
:root, theme-ui, [theme], theme-provider {
|
|
9
9
|
/* ── Accent tints (05 lightest → 95 darkest) ── */
|
|
10
10
|
--a-accent-05-tint: oklch(calc(var(--a-color-l-max) - var(--_sf-05) * (var(--a-color-l-max) - var(--a-color-l-min))) calc(var(--a-accent-c-min) + var(--_cf-05) * (var(--a-accent-c-max) - var(--a-accent-c-min))) var(--a-accent-hue));
|
|
11
11
|
--a-accent-10-tint: oklch(calc(var(--a-color-l-max) - var(--_sf-10) * (var(--a-color-l-max) - var(--a-color-l-min))) calc(var(--a-accent-c-min) + var(--_cf-10) * (var(--a-accent-c-max) - var(--a-accent-c-min))) var(--a-accent-hue));
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Requires primitives-shared.css and parameters.css.
|
|
6
6
|
═══════════════════════════════════════════════════════════════ */
|
|
7
7
|
|
|
8
|
-
:root, theme-ui, [
|
|
8
|
+
:root, theme-ui, [theme], theme-provider {
|
|
9
9
|
/* ── Brand tints (05 lightest → 95 darkest) ── */
|
|
10
10
|
--a-brand-05-tint: oklch(calc(var(--a-color-l-max) - var(--_sf-05) * (var(--a-color-l-max) - var(--a-color-l-min))) calc(var(--a-brand-c-min) + var(--_cf-05) * (var(--a-brand-c-max) - var(--a-brand-c-min))) var(--a-brand-hue));
|
|
11
11
|
--a-brand-10-tint: oklch(calc(var(--a-color-l-max) - var(--_sf-10) * (var(--a-color-l-max) - var(--a-color-l-min))) calc(var(--a-brand-c-min) + var(--_cf-10) * (var(--a-brand-c-max) - var(--a-brand-c-min))) var(--a-brand-hue));
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Requires primitives-shared.css and parameters.css.
|
|
6
6
|
═══════════════════════════════════════════════════════════════ */
|
|
7
7
|
|
|
8
|
-
:root, theme-ui, [
|
|
8
|
+
:root, theme-ui, [theme], theme-provider {
|
|
9
9
|
/* ── Danger tints (05 lightest → 95 darkest) ── */
|
|
10
10
|
--a-danger-05-tint: oklch(calc(var(--a-color-l-max) - var(--_sf-05) * (var(--a-color-l-max) - var(--a-color-l-min))) calc(var(--a-danger-c-min) + var(--_cf-05) * (var(--a-danger-c-max) - var(--a-danger-c-min))) var(--a-danger-hue));
|
|
11
11
|
--a-danger-10-tint: oklch(calc(var(--a-color-l-max) - var(--_sf-10) * (var(--a-color-l-max) - var(--a-color-l-min))) calc(var(--a-danger-c-min) + var(--_cf-10) * (var(--a-danger-c-max) - var(--a-danger-c-min))) var(--a-danger-hue));
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Requires primitives-shared.css and parameters.css.
|
|
6
6
|
═══════════════════════════════════════════════════════════════ */
|
|
7
7
|
|
|
8
|
-
:root, theme-ui, [
|
|
8
|
+
:root, theme-ui, [theme], theme-provider {
|
|
9
9
|
/* ── Info tints (05 lightest → 95 darkest) ── */
|
|
10
10
|
--a-info-05-tint: oklch(calc(var(--a-color-l-max) - var(--_sf-05) * (var(--a-color-l-max) - var(--a-color-l-min))) calc(var(--a-info-c-min) + var(--_cf-05) * (var(--a-info-c-max) - var(--a-info-c-min))) var(--a-info-hue));
|
|
11
11
|
--a-info-10-tint: oklch(calc(var(--a-color-l-max) - var(--_sf-10) * (var(--a-color-l-max) - var(--a-color-l-min))) calc(var(--a-info-c-min) + var(--_cf-10) * (var(--a-info-c-max) - var(--a-info-c-min))) var(--a-info-hue));
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Requires primitives-shared.css and parameters.css.
|
|
6
6
|
═══════════════════════════════════════════════════════════════ */
|
|
7
7
|
|
|
8
|
-
:root, theme-ui, [
|
|
8
|
+
:root, theme-ui, [theme], theme-provider {
|
|
9
9
|
/* ── Neutral tints (05 lightest → 95 darkest) ── */
|
|
10
10
|
--a-neutral-05-tint: oklch(calc(var(--a-color-l-max) - var(--_sf-05) * (var(--a-color-l-max) - var(--a-color-l-min))) calc(var(--a-neutral-c-min) + var(--_cf-05) * (var(--a-neutral-c-max) - var(--a-neutral-c-min))) var(--a-neutral-hue));
|
|
11
11
|
--a-neutral-10-tint: oklch(calc(var(--a-color-l-max) - var(--_sf-10) * (var(--a-color-l-max) - var(--a-color-l-min))) calc(var(--a-neutral-c-min) + var(--_cf-10) * (var(--a-neutral-c-max) - var(--a-neutral-c-min))) var(--a-neutral-hue));
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Requires primitives-shared.css and parameters.css.
|
|
6
6
|
═══════════════════════════════════════════════════════════════ */
|
|
7
7
|
|
|
8
|
-
:root, theme-ui, [
|
|
8
|
+
:root, theme-ui, [theme], theme-provider {
|
|
9
9
|
/* ── Success tints (05 lightest → 95 darkest) ── */
|
|
10
10
|
--a-success-05-tint: oklch(calc(var(--a-color-l-max) - var(--_sf-05) * (var(--a-color-l-max) - var(--a-color-l-min))) calc(var(--a-success-c-min) + var(--_cf-05) * (var(--a-success-c-max) - var(--a-success-c-min))) var(--a-success-hue));
|
|
11
11
|
--a-success-10-tint: oklch(calc(var(--a-color-l-max) - var(--_sf-10) * (var(--a-color-l-max) - var(--a-color-l-min))) calc(var(--a-success-c-min) + var(--_cf-10) * (var(--a-success-c-max) - var(--a-success-c-min))) var(--a-success-hue));
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Requires primitives-shared.css and parameters.css.
|
|
6
6
|
═══════════════════════════════════════════════════════════════ */
|
|
7
7
|
|
|
8
|
-
:root, theme-ui, [
|
|
8
|
+
:root, theme-ui, [theme], theme-provider {
|
|
9
9
|
/* ── Warning tints (05 lightest → 95 darkest) ── */
|
|
10
10
|
--a-warning-05-tint: oklch(calc(var(--a-color-l-max) - var(--_sf-05) * (var(--a-color-l-max) - var(--a-color-l-min)) + var(--_sf-05) * var(--_cf-05) * var(--a-warning-l-lift)) calc(var(--a-warning-c-min) + var(--_cf-05) * (var(--a-warning-c-max) - var(--a-warning-c-min))) var(--a-warning-hue));
|
|
11
11
|
--a-warning-10-tint: oklch(calc(var(--a-color-l-max) - var(--_sf-10) * (var(--a-color-l-max) - var(--a-color-l-min)) + var(--_sf-10) * var(--_cf-10) * var(--a-warning-l-lift)) calc(var(--a-warning-c-min) + var(--_cf-10) * (var(--a-warning-c-max) - var(--a-warning-c-min))) var(--a-warning-hue));
|
package/styles/colors/scrims.css
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
Depends on: parameters.css
|
|
18
18
|
═══════════════════════════════════════════════════════════════ */
|
|
19
19
|
|
|
20
|
-
:root, theme-ui, [
|
|
20
|
+
:root, theme-ui, [theme], theme-provider {
|
|
21
21
|
|
|
22
22
|
/* ── Neutral ── */
|
|
23
23
|
--a-neutral-0-tint-scrim: oklch(var(--a-color-tint-scrim-l) calc(var(--a-neutral-c-min) + 0.0000 * (var(--a-neutral-c-max) - var(--a-neutral-c-min))) var(--a-neutral-hue) / var(--_af-0));
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
base tier deliberately. The base tokens are defined upstream in core.css,
|
|
7
7
|
so removing the override is non-breaking.) */
|
|
8
8
|
|
|
9
|
-
:root, theme-ui, [
|
|
9
|
+
:root, theme-ui, [theme], theme-provider {
|
|
10
10
|
/* ══════════════════════════════════════════════════════════════
|
|
11
11
|
SCRIMS — Role aliases. Map onto the 7-step adaptive scrim-0..6
|
|
12
12
|
scale in scrims.css. Per-family semantic tiers (weak / default /
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
within ONE family for heatmap/choropleth/treemap intensity.
|
|
4
4
|
Split out of semantics.css (v0.6.48 reorg, ADR-0035). */
|
|
5
5
|
|
|
6
|
-
:root, theme-ui, [
|
|
6
|
+
:root, theme-ui, [theme], theme-provider {
|
|
7
7
|
/* ══════════════════════════════════════════════════════════════
|
|
8
8
|
BUCKETS — sequential 5-step ramp per family for data-vis consumers
|
|
9
9
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
the semantics.css barrel so the status-compat overrides in aliases.css
|
|
6
6
|
(imported last) still win, exactly as in the original source order. */
|
|
7
7
|
|
|
8
|
-
:root, theme-ui, [
|
|
8
|
+
:root, theme-ui, [theme], theme-provider {
|
|
9
9
|
|
|
10
10
|
/* ══════════════════════════════════════════════════════════════
|
|
11
11
|
CANVAS — Neutral text, borders, surfaces
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
@property --a-data-8-hue { syntax: "<number>"; inherits: true; initial-value: 313; }
|
|
49
49
|
@property --a-data-9-hue { syntax: "<number>"; inherits: true; initial-value: 335; }
|
|
50
50
|
|
|
51
|
-
:root, theme-ui, [
|
|
51
|
+
:root, theme-ui, [theme], theme-provider {
|
|
52
52
|
/* ══════════════════════════════════════════════════════════════
|
|
53
53
|
DATA PALETTE — 10 categorical series colors for charts
|
|
54
54
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
backgrounds). Contiguous in the original source; split out of
|
|
5
5
|
semantics.css (v0.6.48 reorg, ADR-0035). */
|
|
6
6
|
|
|
7
|
-
:root, theme-ui, [
|
|
7
|
+
:root, theme-ui, [theme], theme-provider {
|
|
8
8
|
/* ══════════════════════════════════════════════════════════════
|
|
9
9
|
FOCUS — canonical ring recipe shared by all form controls
|
|
10
10
|
══════════════════════════════════════════════════════════════
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Neutral role aliases (--a-scrim-dialog etc.) live in semantics.css.
|
|
6
6
|
═══════════════════════════════════════════════════════════════ */
|
|
7
7
|
|
|
8
|
-
:root, theme-ui, [
|
|
8
|
+
:root, theme-ui, [theme], theme-provider {
|
|
9
9
|
|
|
10
10
|
/* ══════════════════════════════════════════════════════════════
|
|
11
11
|
Canvas — neutral surface, semantic alias of --a-neutral-{0-6}
|
package/styles/components.css
CHANGED
|
@@ -27,7 +27,7 @@ function buildCssVarMap() {
|
|
|
27
27
|
// selectors like `[color="info"]` conditionally override --a-fg etc. and
|
|
28
28
|
// must not leak into the default-state token map.
|
|
29
29
|
function isAuthoritativeSelector(sel) {
|
|
30
|
-
return /(^|,)\s*(:root|theme-ui|\[
|
|
30
|
+
return /(^|,)\s*(:root|theme-ui|\[theme\])\b/i.test(sel);
|
|
31
31
|
}
|
|
32
32
|
function walkRules(rules, map) {
|
|
33
33
|
for (const rule of rules) {
|
package/styles/host.css
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
without a specificity fight.
|
|
21
21
|
|
|
22
22
|
── Opt-in add-ons (link separately when wanted) ─────────────────────
|
|
23
|
-
styles/themes.css the [
|
|
23
|
+
styles/themes.css the [theme] preset switcher (@layer context)
|
|
24
24
|
styles/prose.css the [prose] content context (@layer context)
|
|
25
25
|
styles/verse.css the [verse] compact register (opt-in density)
|
|
26
26
|
|
package/styles/prose.css
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
═══════════════════════════════════════════════════════════════ */
|
|
12
12
|
|
|
13
13
|
/* All rules below are the `context` layer (ADR-0038 step 7): theme/prose
|
|
14
|
-
context sits above `utilities`+`components`, so a [
|
|
14
|
+
context sits above `utilities`+`components`, so a [theme]/[prose] scope
|
|
15
15
|
re-skins everything within it. The @layer order is declared by the co-loaded
|
|
16
16
|
barrel (styles/index.css); this file only contributes to `context`. */
|
|
17
17
|
@layer context {
|
package/styles/themes.css
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
/* ═══════════════════════════════════════════════════════════════
|
|
2
|
-
Themes — CSS-only presets via [
|
|
2
|
+
Themes — CSS-only presets via [theme] attribute.
|
|
3
3
|
Zero JavaScript. Apply to any container element.
|
|
4
4
|
|
|
5
5
|
Each theme overrides @property knobs: hues, chroma, radius, spacing.
|
|
6
6
|
Downstream tokens (primitives, surfaces, semantics) recompute automatically.
|
|
7
7
|
|
|
8
8
|
Usage:
|
|
9
|
-
<div
|
|
10
|
-
<html
|
|
9
|
+
<div theme="ocean">...ocean themed...</div>
|
|
10
|
+
<html theme="slate">...entire app...</html>
|
|
11
11
|
═══════════════════════════════════════════════════════════════ */
|
|
12
12
|
|
|
13
13
|
/* All rules below are the `context` layer (ADR-0038 step 7): theme/prose
|
|
14
|
-
context sits above `utilities`+`components`, so a [
|
|
14
|
+
context sits above `utilities`+`components`, so a [theme]/[prose] scope
|
|
15
15
|
re-skins everything within it. The @layer order is declared by the co-loaded
|
|
16
16
|
barrel (styles/index.css); this file only contributes to `context`. */
|
|
17
17
|
@layer context {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
/* ── Default (cool indigo, balanced) ── */
|
|
32
32
|
|
|
33
|
-
[
|
|
33
|
+
[theme="default"] {
|
|
34
34
|
--a-neutral-hue: 225;
|
|
35
35
|
--a-neutral-c-min: 0.010;
|
|
36
36
|
--a-brand-hue: 225;
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/* ── Ocean (cool teal, vivid, rounded, relaxed) ── */
|
|
48
|
-
[
|
|
48
|
+
[theme="ocean"] {
|
|
49
49
|
--a-neutral-hue: 210;
|
|
50
50
|
--a-neutral-c-min: 0.020;
|
|
51
51
|
--a-brand-hue: 200;
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
/* ── Forest (earthy green, muted, compact) ── */
|
|
64
|
-
[
|
|
64
|
+
[theme="forest"] {
|
|
65
65
|
--a-neutral-hue: 150;
|
|
66
66
|
--a-neutral-c-min: 0.018;
|
|
67
67
|
--a-neutral-c-max: 0.030;
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/* ── Sunset (warm amber, vivid, sharp) ── */
|
|
81
|
-
[
|
|
81
|
+
[theme="sunset"] {
|
|
82
82
|
--a-neutral-hue: 30;
|
|
83
83
|
--a-neutral-c-min: 0.022;
|
|
84
84
|
--a-brand-hue: 20;
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/* ── Lavender (soft purple, low chroma, pill radius) ── */
|
|
97
|
-
[
|
|
97
|
+
[theme="lavender"] {
|
|
98
98
|
--a-neutral-hue: 280;
|
|
99
99
|
--a-neutral-c-min: 0.018;
|
|
100
100
|
--a-brand-hue: 270;
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
/* ── Rose (warm pink, medium chroma) ── */
|
|
113
|
-
[
|
|
113
|
+
[theme="rose"] {
|
|
114
114
|
--a-neutral-hue: 350;
|
|
115
115
|
--a-neutral-c-min: 0.016;
|
|
116
116
|
--a-brand-hue: 340;
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
}
|
|
127
127
|
|
|
128
128
|
/* ── Slate (desaturated grey-blue, sharp, tight) ── */
|
|
129
|
-
[
|
|
129
|
+
[theme="slate"] {
|
|
130
130
|
--a-neutral-hue: 220;
|
|
131
131
|
--a-neutral-c-min: 0.008;
|
|
132
132
|
--a-neutral-c-max: 0.015;
|
|
@@ -144,7 +144,7 @@
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
/* ── Midnight (deep indigo, high chroma, rounded, spacious) ── */
|
|
147
|
-
[
|
|
147
|
+
[theme="midnight"] {
|
|
148
148
|
--a-neutral-hue: 250;
|
|
149
149
|
--a-neutral-c-min: 0.020;
|
|
150
150
|
--a-brand-hue: 240;
|
package/styles/verse.css
CHANGED
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
/* ── Radius — verse register: tighter bounds (max 16px). Re-declares the
|
|
51
51
|
clamps so they resolve verse's min/max (base clamps compute at :root). ── */
|
|
52
52
|
--a-radius-min: 0.25rem; /* 4px */
|
|
53
|
-
--a-radius-max: 0.75rem; /*
|
|
53
|
+
--a-radius-max: 0.75rem; /* 12px */
|
|
54
54
|
--a-radius-xs: clamp(var(--a-radius-min), calc(var(--a-radius-k) * var(--a-radius-max) * var(--a-radius-xs-k)), var(--a-radius-max));
|
|
55
55
|
--a-radius-sm: clamp(var(--a-radius-min), calc(var(--a-radius-k) * var(--a-radius-max) * var(--a-radius-sm-k)), var(--a-radius-max));
|
|
56
56
|
--a-radius-md: clamp(var(--a-radius-min), calc(var(--a-radius-k) * var(--a-radius-max) * var(--a-radius-md-k)), var(--a-radius-max));
|
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import { defineTrait } from '../define.js';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
3
|
+
const EDGE = 8; // edge hit-zone thickness
|
|
4
|
+
const CORNER = 14; // corner hit-zone (square, sits on top of the edges it overlaps)
|
|
5
|
+
|
|
6
|
+
// Edges resize one axis; corners resize both (x/y at once). `dw`/`dh` are the sign
|
|
7
|
+
// applied to the pointer delta (dx/dy): +1 grows from this side, -1 grows from the
|
|
8
|
+
// opposite (anchored) side, 0 leaves that axis untouched. Corners are listed LAST so
|
|
9
|
+
// they're appended after the edges and win the hit-test where they overlap.
|
|
10
|
+
const HANDLES = [
|
|
11
|
+
{ name: 'top', dw: 0, dh: -1, cursor: 'ns-resize', geo: { top: '0', left: '0', right: '0', height: `${EDGE}px` } },
|
|
12
|
+
{ name: 'right', dw: 1, dh: 0, cursor: 'ew-resize', geo: { top: '0', right: '0', bottom: '0', width: `${EDGE}px` } },
|
|
13
|
+
{ name: 'bottom', dw: 0, dh: 1, cursor: 'ns-resize', geo: { bottom: '0', left: '0', right: '0', height: `${EDGE}px` } },
|
|
14
|
+
{ name: 'left', dw: -1, dh: 0, cursor: 'ew-resize', geo: { top: '0', left: '0', bottom: '0', width: `${EDGE}px` } },
|
|
15
|
+
{ name: 'top-left', dw: -1, dh: -1, cursor: 'nwse-resize', geo: { top: '0', left: '0', width: `${CORNER}px`, height: `${CORNER}px` } },
|
|
16
|
+
{ name: 'top-right', dw: 1, dh: -1, cursor: 'nesw-resize', geo: { top: '0', right: '0', width: `${CORNER}px`, height: `${CORNER}px` } },
|
|
17
|
+
{ name: 'bottom-right', dw: 1, dh: 1, cursor: 'nwse-resize', geo: { bottom: '0', right: '0', width: `${CORNER}px`, height: `${CORNER}px` } },
|
|
18
|
+
{ name: 'bottom-left', dw: -1, dh: 1, cursor: 'nesw-resize', geo: { bottom: '0', left: '0', width: `${CORNER}px`, height: `${CORNER}px` } },
|
|
19
|
+
];
|
|
5
20
|
|
|
6
21
|
export const resizable = defineTrait({
|
|
7
22
|
name: 'resizable',
|
|
@@ -12,70 +27,68 @@ export const resizable = defineTrait({
|
|
|
12
27
|
config: [],
|
|
13
28
|
setup({ host }) {
|
|
14
29
|
let resizing = false;
|
|
15
|
-
let
|
|
30
|
+
let active = null;
|
|
16
31
|
let startX = 0;
|
|
17
32
|
let startY = 0;
|
|
18
33
|
let startWidth = 0;
|
|
19
34
|
let startHeight = 0;
|
|
20
|
-
let
|
|
21
|
-
let
|
|
35
|
+
let centerX = 0;
|
|
36
|
+
let centerY = 0;
|
|
37
|
+
let symmetric = false;
|
|
22
38
|
const handles = [];
|
|
23
39
|
|
|
24
|
-
// Ensure host is positioned
|
|
25
|
-
|
|
26
|
-
if (pos === 'static') {
|
|
40
|
+
// Ensure host is positioned so the absolute hit-zones anchor to it.
|
|
41
|
+
if (getComputedStyle(host).position === 'static') {
|
|
27
42
|
host.style.position = 'relative';
|
|
28
43
|
}
|
|
29
44
|
|
|
30
|
-
for (const
|
|
45
|
+
for (const h of HANDLES) {
|
|
31
46
|
const handle = document.createElement('div');
|
|
32
|
-
handle.dataset.resizableHandle =
|
|
33
|
-
Object.assign(handle.style, {
|
|
34
|
-
position: 'absolute',
|
|
35
|
-
zIndex: '9999',
|
|
36
|
-
...(edge === 'top' && { top: '0', left: '0', right: '0', height: `${EDGE_SIZE}px`, cursor: 'ns-resize' }),
|
|
37
|
-
...(edge === 'bottom' && { bottom: '0', left: '0', right: '0', height: `${EDGE_SIZE}px`, cursor: 'ns-resize' }),
|
|
38
|
-
...(edge === 'left' && { top: '0', bottom: '0', left: '0', width: `${EDGE_SIZE}px`, cursor: 'ew-resize' }),
|
|
39
|
-
...(edge === 'right' && { top: '0', bottom: '0', right: '0', width: `${EDGE_SIZE}px`, cursor: 'ew-resize' }),
|
|
40
|
-
});
|
|
47
|
+
handle.dataset.resizableHandle = h.name;
|
|
48
|
+
Object.assign(handle.style, { position: 'absolute', zIndex: '9999', cursor: h.cursor, ...h.geo });
|
|
41
49
|
|
|
42
50
|
handle.addEventListener('pointerdown', (e) => {
|
|
43
51
|
e.stopPropagation();
|
|
44
52
|
resizing = true;
|
|
45
|
-
|
|
53
|
+
active = h.name;
|
|
46
54
|
startX = e.clientX;
|
|
47
55
|
startY = e.clientY;
|
|
48
56
|
const rect = host.getBoundingClientRect();
|
|
49
57
|
startWidth = rect.width;
|
|
50
58
|
startHeight = rect.height;
|
|
51
|
-
|
|
52
|
-
|
|
59
|
+
centerX = rect.left + rect.width / 2;
|
|
60
|
+
centerY = rect.top + rect.height / 2;
|
|
61
|
+
// A transform-positioned host re-centers itself as it resizes (e.g.
|
|
62
|
+
// translate(-50%,-50%) centering), so resize SYMMETRICALLY from the center
|
|
63
|
+
// and the dragged edge/corner stays under the cursor. A static/anchored host
|
|
64
|
+
// instead grows from the opposite (fixed) edge.
|
|
65
|
+
symmetric = getComputedStyle(host).transform !== 'none';
|
|
53
66
|
|
|
54
67
|
handle.setPointerCapture(e.pointerId);
|
|
55
68
|
host.setAttribute('data-resizable-resizing', '');
|
|
56
|
-
host.setAttribute('data-resizable-edge',
|
|
69
|
+
host.setAttribute('data-resizable-edge', h.name);
|
|
57
70
|
});
|
|
58
71
|
|
|
59
72
|
handle.addEventListener('pointermove', (e) => {
|
|
60
|
-
if (!resizing ||
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
host.style.
|
|
66
|
-
} else
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
host.style.height = `${Math.max(0, startHeight
|
|
73
|
+
if (!resizing || active !== h.name) return;
|
|
74
|
+
if (symmetric) {
|
|
75
|
+
// Dragged edge/corner tracks the cursor; the host grows from its center
|
|
76
|
+
// (both sides) so the re-centering transform keeps it in place.
|
|
77
|
+
if (h.dw) host.style.width = `${Math.max(0, 2 * h.dw * (e.clientX - centerX))}px`;
|
|
78
|
+
if (h.dh) host.style.height = `${Math.max(0, 2 * h.dh * (e.clientY - centerY))}px`;
|
|
79
|
+
} else {
|
|
80
|
+
// Anchored: the opposite edge stays fixed; the dragged edge grows from it.
|
|
81
|
+
const dx = e.clientX - startX;
|
|
82
|
+
const dy = e.clientY - startY;
|
|
83
|
+
if (h.dw) host.style.width = `${Math.max(0, startWidth + h.dw * dx)}px`;
|
|
84
|
+
if (h.dh) host.style.height = `${Math.max(0, startHeight + h.dh * dy)}px`;
|
|
72
85
|
}
|
|
73
86
|
});
|
|
74
87
|
|
|
75
88
|
handle.addEventListener('pointerup', () => {
|
|
76
|
-
if (!resizing ||
|
|
89
|
+
if (!resizing || active !== h.name) return;
|
|
77
90
|
resizing = false;
|
|
78
|
-
|
|
91
|
+
active = null;
|
|
79
92
|
host.removeAttribute('data-resizable-resizing');
|
|
80
93
|
host.removeAttribute('data-resizable-edge');
|
|
81
94
|
|
|
@@ -17,4 +17,23 @@ describe('resizable', () => {
|
|
|
17
17
|
expect(host.hasAttribute('data-resizable-resizing')).toBe(false);
|
|
18
18
|
expect(host.hasAttribute('data-resizable-edge')).toBe(false);
|
|
19
19
|
});
|
|
20
|
+
|
|
21
|
+
it('creates 8 hit-zones — 4 edges + 4 corners (resize in x, y, and x/y)', () => {
|
|
22
|
+
const host = mountHost();
|
|
23
|
+
connectTrait(resizable, host);
|
|
24
|
+
const names = [...host.querySelectorAll('[data-resizable-handle]')]
|
|
25
|
+
.map((el) => el.dataset.resizableHandle);
|
|
26
|
+
expect(names).toHaveLength(8);
|
|
27
|
+
expect(names).toEqual(expect.arrayContaining([
|
|
28
|
+
'top', 'right', 'bottom', 'left',
|
|
29
|
+
'top-left', 'top-right', 'bottom-right', 'bottom-left',
|
|
30
|
+
]));
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('disconnect removes all hit-zones', () => {
|
|
34
|
+
const host = mountHost();
|
|
35
|
+
const inst = connectTrait(resizable, host);
|
|
36
|
+
inst.disconnect(host);
|
|
37
|
+
expect(host.querySelectorAll('[data-resizable-handle]')).toHaveLength(0);
|
|
38
|
+
});
|
|
20
39
|
});
|