@jsenv/dom 0.7.3 → 0.8.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.
- package/dist/jsenv_dom.js +111 -26
- package/package.json +3 -3
package/dist/jsenv_dom.js
CHANGED
|
@@ -398,7 +398,7 @@ const setStyles = createSetMany$1(setStyle);
|
|
|
398
398
|
const forceStyles = createSetMany$1(forceStyle);
|
|
399
399
|
|
|
400
400
|
// Properties that need px units
|
|
401
|
-
const
|
|
401
|
+
const pxPropertySet = new Set([
|
|
402
402
|
"width",
|
|
403
403
|
"height",
|
|
404
404
|
"top",
|
|
@@ -415,14 +415,13 @@ const pxProperties = [
|
|
|
415
415
|
"paddingRight",
|
|
416
416
|
"paddingBottom",
|
|
417
417
|
"paddingLeft",
|
|
418
|
-
"border",
|
|
419
418
|
"borderWidth",
|
|
420
419
|
"borderTopWidth",
|
|
421
420
|
"borderRightWidth",
|
|
422
421
|
"borderBottomWidth",
|
|
423
422
|
"borderLeftWidth",
|
|
424
423
|
"fontSize",
|
|
425
|
-
|
|
424
|
+
// lineHeight intentionally excluded - it should remain unitless when no unit is specified
|
|
426
425
|
"letterSpacing",
|
|
427
426
|
"wordSpacing",
|
|
428
427
|
"translateX",
|
|
@@ -436,10 +435,10 @@ const pxProperties = [
|
|
|
436
435
|
"gap",
|
|
437
436
|
"rowGap",
|
|
438
437
|
"columnGap",
|
|
439
|
-
];
|
|
438
|
+
]);
|
|
440
439
|
|
|
441
440
|
// Properties that need deg units
|
|
442
|
-
const
|
|
441
|
+
const degPropertySet = new Set([
|
|
443
442
|
"rotate",
|
|
444
443
|
"rotateX",
|
|
445
444
|
"rotateY",
|
|
@@ -447,10 +446,10 @@ const degProperties = [
|
|
|
447
446
|
"skew",
|
|
448
447
|
"skewX",
|
|
449
448
|
"skewY",
|
|
450
|
-
];
|
|
449
|
+
]);
|
|
451
450
|
|
|
452
451
|
// Properties that should remain unitless
|
|
453
|
-
const
|
|
452
|
+
const unitlessPropertySet = new Set([
|
|
454
453
|
"opacity",
|
|
455
454
|
"zIndex",
|
|
456
455
|
"flexGrow",
|
|
@@ -461,7 +460,7 @@ const unitlessProperties = [
|
|
|
461
460
|
"scaleX",
|
|
462
461
|
"scaleY",
|
|
463
462
|
"scaleZ",
|
|
464
|
-
];
|
|
463
|
+
]);
|
|
465
464
|
|
|
466
465
|
// Well-known CSS units and keywords that indicate a value already has proper formatting
|
|
467
466
|
const cssSizeUnitSet = new Set([
|
|
@@ -505,19 +504,25 @@ const cssKeywordSet = new Set([
|
|
|
505
504
|
"revert",
|
|
506
505
|
]);
|
|
507
506
|
|
|
508
|
-
|
|
509
|
-
const hasUnit = (value) => {
|
|
507
|
+
const getUnit = (value) => {
|
|
510
508
|
for (const cssUnit of cssUnitSet) {
|
|
511
509
|
if (value.endsWith(cssUnit)) {
|
|
512
|
-
return
|
|
510
|
+
return cssUnit;
|
|
513
511
|
}
|
|
514
512
|
}
|
|
515
|
-
return
|
|
513
|
+
return "";
|
|
516
514
|
};
|
|
515
|
+
// Check if value already has a unit
|
|
516
|
+
const isUnitless = (value) => getUnit(value) === "";
|
|
517
517
|
const isKeyword = (value) => {
|
|
518
518
|
return cssKeywordSet.has(value);
|
|
519
519
|
};
|
|
520
520
|
|
|
521
|
+
// url(
|
|
522
|
+
// linear-gradient(
|
|
523
|
+
// radial-gradient(
|
|
524
|
+
// ...
|
|
525
|
+
const STARTS_WITH_CSS_IMAGE_FUNCTION_REGEX = /^[a-z-]+\(/;
|
|
521
526
|
// Normalize a single style value
|
|
522
527
|
const normalizeStyle = (value, propertyName, context = "js") => {
|
|
523
528
|
if (propertyName === "transform") {
|
|
@@ -571,21 +576,58 @@ const normalizeStyle = (value, propertyName, context = "js") => {
|
|
|
571
576
|
return undefined;
|
|
572
577
|
}
|
|
573
578
|
|
|
574
|
-
if (
|
|
579
|
+
if (propertyName === "backgroundImage") {
|
|
580
|
+
if (context === "css") {
|
|
581
|
+
if (
|
|
582
|
+
typeof value === "string" &&
|
|
583
|
+
!isKeyword(value) &&
|
|
584
|
+
!STARTS_WITH_CSS_IMAGE_FUNCTION_REGEX.test(value)
|
|
585
|
+
) {
|
|
586
|
+
return `url(${value})`;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return value;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
if (propertyName === "lineHeight") {
|
|
593
|
+
if (context === "js") {
|
|
594
|
+
if (typeof value === "string") {
|
|
595
|
+
const unit = getUnit(value);
|
|
596
|
+
if (unit === "px") {
|
|
597
|
+
const float = parseFloat(value);
|
|
598
|
+
return float;
|
|
599
|
+
}
|
|
600
|
+
if (unit === "") {
|
|
601
|
+
return `${value}em`;
|
|
602
|
+
}
|
|
603
|
+
return value;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
if (context === "css") {
|
|
607
|
+
if (typeof value === "number") {
|
|
608
|
+
// When line height is converted to a number it means
|
|
609
|
+
// it was in pixels, we must restore the unit
|
|
610
|
+
return `${value}px`;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
return value;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (pxPropertySet.has(propertyName)) {
|
|
575
617
|
return normalizeNumber(value, {
|
|
576
618
|
propertyName,
|
|
577
619
|
unit: "px",
|
|
578
620
|
preferedType: context === "js" ? "number" : "string",
|
|
579
621
|
});
|
|
580
622
|
}
|
|
581
|
-
if (
|
|
623
|
+
if (degPropertySet.has(propertyName)) {
|
|
582
624
|
return normalizeNumber(value, {
|
|
583
625
|
propertyName,
|
|
584
626
|
unit: "deg",
|
|
585
627
|
preferedType: "string",
|
|
586
628
|
});
|
|
587
629
|
}
|
|
588
|
-
if (
|
|
630
|
+
if (unitlessPropertySet.has(propertyName)) {
|
|
589
631
|
return normalizeNumber(value, {
|
|
590
632
|
propertyName,
|
|
591
633
|
unit: "",
|
|
@@ -599,7 +641,7 @@ const normalizeNumber = (value, { unit, propertyName, preferedType }) => {
|
|
|
599
641
|
if (typeof value === "string") {
|
|
600
642
|
// Keep strings as-is (including %, em, rem, auto, none, etc.)
|
|
601
643
|
if (preferedType === "string") {
|
|
602
|
-
if (unit &&
|
|
644
|
+
if (unit && isUnitless(value) && !isKeyword(value)) {
|
|
603
645
|
return `${value}${unit}`;
|
|
604
646
|
}
|
|
605
647
|
return value;
|
|
@@ -646,6 +688,9 @@ const normalizeStyles = (styles, context = "js", mutate = false) => {
|
|
|
646
688
|
const normalized = {};
|
|
647
689
|
for (const key of Object.keys(styles)) {
|
|
648
690
|
const value = styles[key];
|
|
691
|
+
if (value === undefined) {
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
649
694
|
normalized[key] = normalizeStyle(value, key, context);
|
|
650
695
|
}
|
|
651
696
|
return normalized;
|
|
@@ -860,7 +905,7 @@ const parseSimple2DMatrix = (a, b, c, d, e, f) => {
|
|
|
860
905
|
};
|
|
861
906
|
|
|
862
907
|
// Merge two style objects, handling special cases like transform
|
|
863
|
-
const
|
|
908
|
+
const mergeTwoStyles = (stylesA, stylesB, context = "js") => {
|
|
864
909
|
if (!stylesA) {
|
|
865
910
|
return normalizeStyles(stylesB, context);
|
|
866
911
|
}
|
|
@@ -1100,7 +1145,7 @@ const createStyleController = (name = "anonymous") => {
|
|
|
1100
1145
|
}
|
|
1101
1146
|
|
|
1102
1147
|
const { styles, animation } = elementData;
|
|
1103
|
-
const mergedStyles =
|
|
1148
|
+
const mergedStyles = mergeTwoStyles(styles, stylesToSet);
|
|
1104
1149
|
elementData.styles = mergedStyles;
|
|
1105
1150
|
updateAnimationStyles(animation, mergedStyles);
|
|
1106
1151
|
};
|
|
@@ -1330,8 +1375,12 @@ const createStyleController = (name = "anonymous") => {
|
|
|
1330
1375
|
return controller;
|
|
1331
1376
|
};
|
|
1332
1377
|
|
|
1378
|
+
const getStyleForKeyframe = (styles) => {
|
|
1379
|
+
const cssStyles = normalizeStyles(styles, "css");
|
|
1380
|
+
return cssStyles;
|
|
1381
|
+
};
|
|
1333
1382
|
const createAnimationForStyles = (element, styles, id) => {
|
|
1334
|
-
const cssStylesToSet =
|
|
1383
|
+
const cssStylesToSet = getStyleForKeyframe(styles);
|
|
1335
1384
|
const animation = element.animate([cssStylesToSet], {
|
|
1336
1385
|
duration: 0,
|
|
1337
1386
|
fill: "forwards",
|
|
@@ -1343,7 +1392,7 @@ const createAnimationForStyles = (element, styles, id) => {
|
|
|
1343
1392
|
};
|
|
1344
1393
|
|
|
1345
1394
|
const updateAnimationStyles = (animation, styles) => {
|
|
1346
|
-
const cssStyles =
|
|
1395
|
+
const cssStyles = getStyleForKeyframe(styles);
|
|
1347
1396
|
animation.effect.setKeyframes([cssStyles]);
|
|
1348
1397
|
animation.play();
|
|
1349
1398
|
animation.pause();
|
|
@@ -2131,6 +2180,11 @@ const resolveCSSColor = (color, element, context = "js") => {
|
|
|
2131
2180
|
}
|
|
2132
2181
|
}
|
|
2133
2182
|
|
|
2183
|
+
if (color.startsWith("--")) {
|
|
2184
|
+
console.warn(`found "${color}". Use "var(${color})" instead.`);
|
|
2185
|
+
return null;
|
|
2186
|
+
}
|
|
2187
|
+
|
|
2134
2188
|
// Parse the resolved color and return in the requested format
|
|
2135
2189
|
const rgba = parseCSSColor(resolvedColor);
|
|
2136
2190
|
|
|
@@ -2143,18 +2197,24 @@ const resolveCSSColor = (color, element, context = "js") => {
|
|
|
2143
2197
|
|
|
2144
2198
|
/**
|
|
2145
2199
|
* Chooses between light and dark colors based on which provides better contrast against a background
|
|
2146
|
-
* @param {
|
|
2147
|
-
* @param {string}
|
|
2148
|
-
* @param {string}
|
|
2149
|
-
* @param {
|
|
2200
|
+
* @param {string} backgroundColor - CSS color value (hex, rgb, hsl, CSS variable, etc.) to test against
|
|
2201
|
+
* @param {string} [lightColor="white"] - Light color option (typically for dark backgrounds)
|
|
2202
|
+
* @param {string} [darkColor="black"] - Dark color option (typically for light backgrounds)
|
|
2203
|
+
* @param {Element} [element] - DOM element to resolve CSS variables against
|
|
2150
2204
|
* @returns {string} The color that provides better contrast (lightColor or darkColor)
|
|
2205
|
+
* @example
|
|
2206
|
+
* // Choose text color for a dark blue background
|
|
2207
|
+
* pickLightOrDark("#1a202c") // returns "white"
|
|
2208
|
+
*
|
|
2209
|
+
* // Choose text color for a light background with CSS variable
|
|
2210
|
+
* pickLightOrDark("var(--bg-color)", "white", "black", element) // returns "black" or "white"
|
|
2151
2211
|
*/
|
|
2152
2212
|
|
|
2153
2213
|
const pickLightOrDark = (
|
|
2154
|
-
element,
|
|
2155
2214
|
backgroundColor,
|
|
2156
2215
|
lightColor = "white",
|
|
2157
2216
|
darkColor = "black",
|
|
2217
|
+
element,
|
|
2158
2218
|
) => {
|
|
2159
2219
|
const resolvedBgColor = resolveCSSColor(backgroundColor, element);
|
|
2160
2220
|
const resolvedLightColor = resolveCSSColor(lightColor, element);
|
|
@@ -2174,6 +2234,31 @@ const pickLightOrDark = (
|
|
|
2174
2234
|
return contrastWithLight > contrastWithDark ? lightColor : darkColor;
|
|
2175
2235
|
};
|
|
2176
2236
|
|
|
2237
|
+
/**
|
|
2238
|
+
* Resolves the luminance value of a CSS color
|
|
2239
|
+
* @param {string} color - CSS color value (hex, rgb, hsl, CSS variable, etc.)
|
|
2240
|
+
* @param {Element} [element] - DOM element to resolve CSS variables against
|
|
2241
|
+
* @returns {number|undefined} Relative luminance (0-1) according to WCAG formula, or undefined if color cannot be resolved
|
|
2242
|
+
* @example
|
|
2243
|
+
* // Get luminance of a hex color
|
|
2244
|
+
* resolveColorLuminance("#ff0000") // returns ~0.213 (red)
|
|
2245
|
+
*
|
|
2246
|
+
* // Get luminance of a CSS variable
|
|
2247
|
+
* resolveColorLuminance("var(--primary-color)", element) // returns luminance value or undefined
|
|
2248
|
+
*
|
|
2249
|
+
* // Use for light/dark classification
|
|
2250
|
+
* const luminance = resolveColorLuminance("#2ecc71");
|
|
2251
|
+
* const isLight = luminance > 0.3; // true for light colors, false for dark
|
|
2252
|
+
*/
|
|
2253
|
+
const resolveColorLuminance = (color, element) => {
|
|
2254
|
+
const resolvedColor = resolveCSSColor(color, element);
|
|
2255
|
+
if (!resolvedColor) {
|
|
2256
|
+
return undefined;
|
|
2257
|
+
}
|
|
2258
|
+
const [r, g, b] = resolvedColor;
|
|
2259
|
+
return getLuminance(r, g, b);
|
|
2260
|
+
};
|
|
2261
|
+
|
|
2177
2262
|
const findAncestor = (node, predicate) => {
|
|
2178
2263
|
let ancestor = node.parentNode;
|
|
2179
2264
|
while (ancestor) {
|
|
@@ -11785,4 +11870,4 @@ const notifyTransitionOverflow = (element, transitionId) => {
|
|
|
11785
11870
|
};
|
|
11786
11871
|
};
|
|
11787
11872
|
|
|
11788
|
-
export { EASING, activeElementSignal, addActiveElementEffect, addAttributeEffect, addWillChange, allowWheelThrough, appendStyles, canInterceptKeys, captureScrollState, createDragGestureController, createDragToMoveGestureController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createValueEffect, createWidthTransition, cubicBezier, dragAfterThreshold, elementIsFocusable, elementIsVisibleForFocus, elementIsVisuallyVisible, findAfter, findAncestor, findBefore, findDescendant, findFocusable, getAvailableHeight, getAvailableWidth, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getElementSignature, getFirstVisuallyVisibleAncestor, getFocusVisibilityInfo, getHeight, getInnerHeight, getInnerWidth, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getOpacity, getPaddingSizes, getPositionedParent, getPreferedColorScheme, getScrollContainer, getScrollContainerSet, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getTranslateX, getTranslateY, getVisuallyVisibleInfo, getWidth, initFlexDetailsSet, initFocusGroup, initPositionSticky, initUITransition, isScrollable,
|
|
11873
|
+
export { EASING, activeElementSignal, addActiveElementEffect, addAttributeEffect, addWillChange, allowWheelThrough, appendStyles, canInterceptKeys, captureScrollState, createDragGestureController, createDragToMoveGestureController, createHeightTransition, createIterableWeakSet, createOpacityTransition, createPubSub, createStyleController, createTimelineTransition, createTransition, createTranslateXTransition, createValueEffect, createWidthTransition, cubicBezier, dragAfterThreshold, elementIsFocusable, elementIsVisibleForFocus, elementIsVisuallyVisible, findAfter, findAncestor, findBefore, findDescendant, findFocusable, getAvailableHeight, getAvailableWidth, getBorderSizes, getContrastRatio, getDefaultStyles, getDragCoordinates, getDropTargetInfo, getElementSignature, getFirstVisuallyVisibleAncestor, getFocusVisibilityInfo, getHeight, getInnerHeight, getInnerWidth, getLuminance, getMarginSizes, getMaxHeight, getMaxWidth, getMinHeight, getMinWidth, getOpacity, getPaddingSizes, getPositionedParent, getPreferedColorScheme, getScrollContainer, getScrollContainerSet, getScrollRelativeRect, getSelfAndAncestorScrolls, getStyle, getTranslateX, getTranslateY, getVisuallyVisibleInfo, getWidth, initFlexDetailsSet, initFocusGroup, initPositionSticky, initUITransition, isScrollable, mergeTwoStyles, normalizeStyle, normalizeStyles, parseCSSColor, pickLightOrDark, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, resolveCSSColor, resolveCSSSize, resolveColorLuminance, setAttribute, setAttributes, setStyles, startDragToResizeGesture, stickyAsRelativeCoords, stringifyCSSColor, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/dom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "DOM utilities for writing frontend code",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@jsenv/core": "../../../",
|
|
38
38
|
"@jsenv/navi": "../navi",
|
|
39
|
-
"@preact/signals": "
|
|
40
|
-
"preact": "
|
|
39
|
+
"@preact/signals": "2.4.0",
|
|
40
|
+
"preact": "11.0.0-beta.0"
|
|
41
41
|
},
|
|
42
42
|
"publishConfig": {
|
|
43
43
|
"access": "public"
|