actual-css 0.14.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +4 -4
- package/dist/actual.full.css +4 -0
- package/dist/actual.full.js +1 -1
- package/dist/actual.full.min.css +1 -1
- package/docs/design-notes/widget-primitives.md +4 -0
- package/docs/pages/enhancements/flyout.md +4 -3
- package/docs/pages/enhancements/overview.md +2 -1
- package/docs/pages/enhancements/tooltip.md +3 -1
- package/docs/pages/examples/overview.md +2 -2
- package/package.json +4 -3
- package/src/css/forms/color-swatch.css +4 -0
- package/src/js/enhance.js +14 -0
- package/src/js/floating-space.js +26 -0
- package/src/js/flyout.js +3 -1
- package/src/js/parse-config.js +5 -2
- package/src/js/tooltip.js +2 -2
|
@@ -35,6 +35,10 @@ The named exports each module carries:
|
|
|
35
35
|
- `selectors` — `CLASSES`
|
|
36
36
|
|
|
37
37
|
`surface` also owns outside-click and Escape dismissal, and focus restore.
|
|
38
|
+
A click on an element with `data-flyout-close` closes the open panel around
|
|
39
|
+
it, whichever widget opened that panel. The attribute keeps the `flyout` name
|
|
40
|
+
because the panels authors write are flyouts; `surface` is only the code that
|
|
41
|
+
opens and closes them.
|
|
38
42
|
`menu` filters to usable items.
|
|
39
43
|
|
|
40
44
|
## Focus group
|
|
@@ -67,9 +67,10 @@ For a modal bottom sheet or action sheet, use `dialog.drawer`; use
|
|
|
67
67
|
`dialog.modal` for a centered modal. Those components own modal focus,
|
|
68
68
|
dismissal, scroll locking, and backdrop behavior explicitly.
|
|
69
69
|
|
|
70
|
-
Use `data-flyout-placement` for the preferred anchored placement
|
|
71
|
-
|
|
72
|
-
`bottom-
|
|
70
|
+
Use `data-flyout-placement` for the preferred anchored placement: a side
|
|
71
|
+
(`top`, `bottom`, `left`, `right`), optionally followed by `-start` or `-end`,
|
|
72
|
+
such as `bottom-start` or `right`. An unknown value logs a console warning and
|
|
73
|
+
falls back to `bottom-start`.
|
|
73
74
|
|
|
74
75
|
Use `data-flyout-distance` for the trigger gap in pixels. The default is `4`.
|
|
75
76
|
|
|
@@ -56,7 +56,8 @@ is a no-op until the module is loaded again with a DOM.
|
|
|
56
56
|
Load the full bundle **or** the source modules, never both: each evaluation
|
|
57
57
|
owns its registry and document handlers, so a dist bundle mixed with
|
|
58
58
|
`actual-css/js/*` source subpaths (or two dist bundles) registers every
|
|
59
|
-
behavior twice and a flyout opens then immediately closes.
|
|
59
|
+
behavior twice and a flyout opens then immediately closes. The second copy
|
|
60
|
+
logs a console warning when it loads.
|
|
60
61
|
|
|
61
62
|
## Component pages
|
|
62
63
|
|
|
@@ -115,7 +115,9 @@ viewport-aware maximum width.
|
|
|
115
115
|
## Positioning
|
|
116
116
|
|
|
117
117
|
Set `data-tooltip-placement` to control where the tooltip appears relative to
|
|
118
|
-
its trigger
|
|
118
|
+
its trigger: a side (`top`, `bottom`, `left`, `right`), optionally followed by
|
|
119
|
+
`-start` or `-end`. An unknown value logs a console warning and falls back to
|
|
120
|
+
`top`. The arrow follows the placement automatically.
|
|
119
121
|
|
|
120
122
|
The runtime also picks the coordinate space, and writes `position` to match. A
|
|
121
123
|
tooltip whose trigger scrolls with the page is placed in document coordinates
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
The examples live in the repository under `demo/` and load `actual.full.css`
|
|
6
6
|
and `actual.full.js` from `dist/`, plus the theme palettes bundle from
|
|
7
|
-
`demo/assets/`.
|
|
8
|
-
`bun run build:
|
|
7
|
+
`demo/assets/`. Both are build output: in a clone, run `bun run build:dist`,
|
|
8
|
+
`bun run build:js` and `bun run build:themes` before opening a page.
|
|
9
9
|
|
|
10
10
|
## Templates
|
|
11
11
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "actual-css",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.1",
|
|
4
4
|
"description": "Plain CSS component framework with semantic classes, universal variants, small tokens, theme hooks, and progressive enhancements.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"packageManager": "bun@1.4.3",
|
|
@@ -132,6 +132,7 @@
|
|
|
132
132
|
"build:js": "bun scripts/build-js.js",
|
|
133
133
|
"build:themes": "bun scripts/build-themes.js",
|
|
134
134
|
"build:size": "bun scripts/build-size.js",
|
|
135
|
+
"build:pages": "bun scripts/pages.js",
|
|
135
136
|
"export:design": "bun scripts/export-design.js",
|
|
136
137
|
"check:enhance": "bun run scripts/check-enhance.js",
|
|
137
138
|
"generate:reserved": "bun scripts/generate-reserved-classes.js",
|
|
@@ -157,9 +158,9 @@
|
|
|
157
158
|
},
|
|
158
159
|
"devDependencies": {
|
|
159
160
|
"@biomejs/biome": "^2.5.15",
|
|
160
|
-
"happy-dom": "^
|
|
161
|
+
"happy-dom": "^21.0.1"
|
|
161
162
|
},
|
|
162
163
|
"dependencies": {
|
|
163
|
-
"@lekoala/floating": "^0.
|
|
164
|
+
"@lekoala/floating": "^0.3.0"
|
|
164
165
|
}
|
|
165
166
|
}
|
|
@@ -49,6 +49,10 @@
|
|
|
49
49
|
its invisible reach. */
|
|
50
50
|
margin: calc(var(--color-swatch-reach) / -2);
|
|
51
51
|
padding: 0;
|
|
52
|
+
/* No in-flow text, so the baseline would be the box's bottom edge and an
|
|
53
|
+
inline swatch would ride above the line (a flyout trigger beside its
|
|
54
|
+
label). A palette in .cluster or .grid ignores it. */
|
|
55
|
+
vertical-align: middle;
|
|
52
56
|
border: 0;
|
|
53
57
|
background: transparent;
|
|
54
58
|
color: inherit;
|
package/src/js/enhance.js
CHANGED
|
@@ -31,6 +31,20 @@
|
|
|
31
31
|
const registries = new WeakMap();
|
|
32
32
|
const ownedNames = new WeakMap();
|
|
33
33
|
|
|
34
|
+
// Each evaluation owns its registries, so a second copy (the dist bundle plus
|
|
35
|
+
// source modules, or two bundles) registers every behavior twice and fails
|
|
36
|
+
// silently — a flyout opens then closes. Every built-in imports this module,
|
|
37
|
+
// so the marker catches any duplicated runtime once, here.
|
|
38
|
+
const RUNTIME = Symbol.for("actual-css.runtime");
|
|
39
|
+
if (typeof document !== "undefined") {
|
|
40
|
+
if (globalThis[RUNTIME]) {
|
|
41
|
+
console.warn(
|
|
42
|
+
"actual-css: a second copy of the JavaScript runtime is loaded. Load the full bundle or the source modules, never both.",
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
globalThis[RUNTIME] = true;
|
|
46
|
+
}
|
|
47
|
+
|
|
34
48
|
// Node type constants as literals, not the realm's Node global, so nodes from
|
|
35
49
|
// another document/window compare correctly.
|
|
36
50
|
const ELEMENT_NODE = 1;
|
package/src/js/floating-space.js
CHANGED
|
@@ -29,6 +29,32 @@ function isViewportAnchored(anchor) {
|
|
|
29
29
|
return false;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// Mirrors the placements @lekoala/floating accepts; it throws on any other
|
|
33
|
+
// value, and from an autoUpdate callback that throw would repeat on every
|
|
34
|
+
// scroll. Markup is validated here instead, so a typo warns once.
|
|
35
|
+
const PLACEMENTS = new Set(
|
|
36
|
+
["top", "bottom", "left", "right"].flatMap((side) => [side, `${side}-start`, `${side}-end`]),
|
|
37
|
+
);
|
|
38
|
+
const warnedPlacements = new WeakSet();
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The author's placement attribute, or null when absent or unknown.
|
|
42
|
+
*
|
|
43
|
+
* @param {Element | null | undefined} el
|
|
44
|
+
* @param {string} attribute
|
|
45
|
+
* @returns {string | null}
|
|
46
|
+
*/
|
|
47
|
+
export function readPlacement(el, attribute) {
|
|
48
|
+
const value = el?.getAttribute(attribute);
|
|
49
|
+
if (!value) return null;
|
|
50
|
+
if (PLACEMENTS.has(value)) return value;
|
|
51
|
+
if (!warnedPlacements.has(el)) {
|
|
52
|
+
warnedPlacements.add(el);
|
|
53
|
+
console.warn(`Unknown ${attribute}="${value}" ignored`, el);
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
32
58
|
/**
|
|
33
59
|
* @param {Element | null | undefined} anchor
|
|
34
60
|
* @returns {{ space: "viewport" | "document", position: "fixed" | "absolute" }}
|
package/src/js/flyout.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { registerEnhancement } from "./enhance.js";
|
|
2
|
+
import { readPlacement } from "./floating-space.js";
|
|
2
3
|
import { focusFirstDescendant } from "./focus.js";
|
|
3
4
|
import { shouldIgnoreKey } from "./keys.js";
|
|
4
5
|
import { connectMenu, focusFirstMenuItem, focusLastMenuItem } from "./menu.js";
|
|
@@ -9,7 +10,8 @@ const triggerMap = new WeakMap();
|
|
|
9
10
|
function readFlyoutOptions(panel) {
|
|
10
11
|
const opts = { autoClose: "true" };
|
|
11
12
|
const ds = panel.dataset;
|
|
12
|
-
|
|
13
|
+
const placement = readPlacement(panel, "data-flyout-placement");
|
|
14
|
+
if (placement) opts.placement = placement;
|
|
13
15
|
if (ds.flyoutDistance != null) {
|
|
14
16
|
const d = Number.parseFloat(ds.flyoutDistance);
|
|
15
17
|
if (Number.isFinite(d)) opts.distance = d;
|
package/src/js/parse-config.js
CHANGED
|
@@ -11,10 +11,13 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
function relax(source) {
|
|
14
|
+
// Double-quoted strings are matched first and copied verbatim, so text
|
|
15
|
+
// such as `http:` inside them is never mistaken for an unquoted key.
|
|
14
16
|
return source.replace(
|
|
15
|
-
/([a-zA-Z_$][\w$-]*)\s*:|'((?:\\'|[^'])*)'/g,
|
|
16
|
-
(
|
|
17
|
+
/"(?:\\.|[^"\\])*"|([a-zA-Z_$][\w$-]*)\s*:|'((?:\\'|[^'])*)'/g,
|
|
18
|
+
(match, key, stringContent) => {
|
|
17
19
|
if (key) return `"${key}":`;
|
|
20
|
+
if (stringContent === undefined) return match;
|
|
18
21
|
const unescaped = stringContent.replace(/\\'/g, "'");
|
|
19
22
|
const escaped = unescaped.replace(/"/g, '\\"');
|
|
20
23
|
return `"${escaped}"`;
|
package/src/js/tooltip.js
CHANGED
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
import { autoUpdate, reposition } from "@lekoala/floating";
|
|
52
52
|
import enhance from "./enhance.js";
|
|
53
53
|
import { registerEscapeDismissal } from "./escape.js";
|
|
54
|
-
import { positionModeFor } from "./floating-space.js";
|
|
54
|
+
import { positionModeFor, readPlacement } from "./floating-space.js";
|
|
55
55
|
import { CLASSES } from "./selectors.js";
|
|
56
56
|
|
|
57
57
|
const SHOW_DELAY_MS = 150;
|
|
@@ -144,7 +144,7 @@ export function isTooltipVisible(tip) {
|
|
|
144
144
|
* the tip's own placement never enters into it.
|
|
145
145
|
*/
|
|
146
146
|
function placementFor(ref) {
|
|
147
|
-
return ref
|
|
147
|
+
return readPlacement(ref, "data-tooltip-placement") ?? "top";
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
function hasOption(trigger, name) {
|