@jsenv/dom 0.7.4 → 0.8.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.
Files changed (2) hide show
  1. package/dist/jsenv_dom.js +98 -27
  2. package/package.json +3 -3
package/dist/jsenv_dom.js CHANGED
@@ -397,10 +397,14 @@ const createSetMany$1 = (setter) => {
397
397
  const setStyles = createSetMany$1(setStyle);
398
398
  const forceStyles = createSetMany$1(forceStyle);
399
399
 
400
- // Properties that need px units
401
- const pxProperties = [
400
+ // Properties that can use px units
401
+ const pxPropertySet = new Set([
402
402
  "width",
403
403
  "height",
404
+ "minWidth",
405
+ "maxWidth",
406
+ "minHeight",
407
+ "maxHeight",
404
408
  "top",
405
409
  "left",
406
410
  "right",
@@ -415,14 +419,13 @@ const pxProperties = [
415
419
  "paddingRight",
416
420
  "paddingBottom",
417
421
  "paddingLeft",
418
- "border",
419
422
  "borderWidth",
420
423
  "borderTopWidth",
421
424
  "borderRightWidth",
422
425
  "borderBottomWidth",
423
426
  "borderLeftWidth",
424
427
  "fontSize",
425
- "lineHeight",
428
+ // lineHeight intentionally excluded - it should remain unitless when no unit is specified
426
429
  "letterSpacing",
427
430
  "wordSpacing",
428
431
  "translateX",
@@ -436,10 +439,10 @@ const pxProperties = [
436
439
  "gap",
437
440
  "rowGap",
438
441
  "columnGap",
439
- ];
442
+ ]);
440
443
 
441
444
  // Properties that need deg units
442
- const degProperties = [
445
+ const degPropertySet = new Set([
443
446
  "rotate",
444
447
  "rotateX",
445
448
  "rotateY",
@@ -447,10 +450,10 @@ const degProperties = [
447
450
  "skew",
448
451
  "skewX",
449
452
  "skewY",
450
- ];
453
+ ]);
451
454
 
452
455
  // Properties that should remain unitless
453
- const unitlessProperties = [
456
+ const unitlessPropertySet = new Set([
454
457
  "opacity",
455
458
  "zIndex",
456
459
  "flexGrow",
@@ -461,7 +464,7 @@ const unitlessProperties = [
461
464
  "scaleX",
462
465
  "scaleY",
463
466
  "scaleZ",
464
- ];
467
+ ]);
465
468
 
466
469
  // Well-known CSS units and keywords that indicate a value already has proper formatting
467
470
  const cssSizeUnitSet = new Set([
@@ -505,15 +508,16 @@ const cssKeywordSet = new Set([
505
508
  "revert",
506
509
  ]);
507
510
 
508
- // Check if value already has a unit or is a keyword
509
- const hasUnit = (value) => {
511
+ const getUnit = (value) => {
510
512
  for (const cssUnit of cssUnitSet) {
511
513
  if (value.endsWith(cssUnit)) {
512
- return true;
514
+ return cssUnit;
513
515
  }
514
516
  }
515
- return false;
517
+ return "";
516
518
  };
519
+ // Check if value already has a unit
520
+ const isUnitless = (value) => getUnit(value) === "";
517
521
  const isKeyword = (value) => {
518
522
  return cssKeywordSet.has(value);
519
523
  };
@@ -589,21 +593,45 @@ const normalizeStyle = (value, propertyName, context = "js") => {
589
593
  return value;
590
594
  }
591
595
 
592
- if (pxProperties.includes(propertyName)) {
596
+ if (propertyName === "lineHeight") {
597
+ if (context === "js") {
598
+ if (typeof value === "string") {
599
+ const unit = getUnit(value);
600
+ if (unit === "px") {
601
+ const float = parseFloat(value);
602
+ return float;
603
+ }
604
+ if (unit === "") {
605
+ return `${value}em`;
606
+ }
607
+ return value;
608
+ }
609
+ }
610
+ if (context === "css") {
611
+ if (typeof value === "number") {
612
+ // When line height is converted to a number it means
613
+ // it was in pixels, we must restore the unit
614
+ return `${value}px`;
615
+ }
616
+ }
617
+ return value;
618
+ }
619
+
620
+ if (pxPropertySet.has(propertyName)) {
593
621
  return normalizeNumber(value, {
594
622
  propertyName,
595
623
  unit: "px",
596
624
  preferedType: context === "js" ? "number" : "string",
597
625
  });
598
626
  }
599
- if (degProperties.includes(propertyName)) {
627
+ if (degPropertySet.has(propertyName)) {
600
628
  return normalizeNumber(value, {
601
629
  propertyName,
602
630
  unit: "deg",
603
631
  preferedType: "string",
604
632
  });
605
633
  }
606
- if (unitlessProperties.includes(propertyName)) {
634
+ if (unitlessPropertySet.has(propertyName)) {
607
635
  return normalizeNumber(value, {
608
636
  propertyName,
609
637
  unit: "",
@@ -617,7 +645,7 @@ const normalizeNumber = (value, { unit, propertyName, preferedType }) => {
617
645
  if (typeof value === "string") {
618
646
  // Keep strings as-is (including %, em, rem, auto, none, etc.)
619
647
  if (preferedType === "string") {
620
- if (unit && !hasUnit(value) && !isKeyword(value)) {
648
+ if (unit && isUnitless(value) && !isKeyword(value)) {
621
649
  return `${value}${unit}`;
622
650
  }
623
651
  return value;
@@ -664,6 +692,9 @@ const normalizeStyles = (styles, context = "js", mutate = false) => {
664
692
  const normalized = {};
665
693
  for (const key of Object.keys(styles)) {
666
694
  const value = styles[key];
695
+ if (value === undefined) {
696
+ continue;
697
+ }
667
698
  normalized[key] = normalizeStyle(value, key, context);
668
699
  }
669
700
  return normalized;
@@ -878,7 +909,7 @@ const parseSimple2DMatrix = (a, b, c, d, e, f) => {
878
909
  };
879
910
 
880
911
  // Merge two style objects, handling special cases like transform
881
- const mergeStyles = (stylesA, stylesB, context = "js") => {
912
+ const mergeTwoStyles = (stylesA, stylesB, context = "js") => {
882
913
  if (!stylesA) {
883
914
  return normalizeStyles(stylesB, context);
884
915
  }
@@ -1118,7 +1149,7 @@ const createStyleController = (name = "anonymous") => {
1118
1149
  }
1119
1150
 
1120
1151
  const { styles, animation } = elementData;
1121
- const mergedStyles = mergeStyles(styles, stylesToSet);
1152
+ const mergedStyles = mergeTwoStyles(styles, stylesToSet);
1122
1153
  elementData.styles = mergedStyles;
1123
1154
  updateAnimationStyles(animation, mergedStyles);
1124
1155
  };
@@ -1348,8 +1379,12 @@ const createStyleController = (name = "anonymous") => {
1348
1379
  return controller;
1349
1380
  };
1350
1381
 
1382
+ const getStyleForKeyframe = (styles) => {
1383
+ const cssStyles = normalizeStyles(styles, "css");
1384
+ return cssStyles;
1385
+ };
1351
1386
  const createAnimationForStyles = (element, styles, id) => {
1352
- const cssStylesToSet = normalizeStyles(styles, "css");
1387
+ const cssStylesToSet = getStyleForKeyframe(styles);
1353
1388
  const animation = element.animate([cssStylesToSet], {
1354
1389
  duration: 0,
1355
1390
  fill: "forwards",
@@ -1361,7 +1396,7 @@ const createAnimationForStyles = (element, styles, id) => {
1361
1396
  };
1362
1397
 
1363
1398
  const updateAnimationStyles = (animation, styles) => {
1364
- const cssStyles = normalizeStyles(styles, "css");
1399
+ const cssStyles = getStyleForKeyframe(styles);
1365
1400
  animation.effect.setKeyframes([cssStyles]);
1366
1401
  animation.play();
1367
1402
  animation.pause();
@@ -2149,6 +2184,11 @@ const resolveCSSColor = (color, element, context = "js") => {
2149
2184
  }
2150
2185
  }
2151
2186
 
2187
+ if (color.startsWith("--")) {
2188
+ console.warn(`found "${color}". Use "var(${color})" instead.`);
2189
+ return null;
2190
+ }
2191
+
2152
2192
  // Parse the resolved color and return in the requested format
2153
2193
  const rgba = parseCSSColor(resolvedColor);
2154
2194
 
@@ -2161,18 +2201,24 @@ const resolveCSSColor = (color, element, context = "js") => {
2161
2201
 
2162
2202
  /**
2163
2203
  * Chooses between light and dark colors based on which provides better contrast against a background
2164
- * @param {Element} element - DOM element to resolve CSS variables against
2165
- * @param {string} backgroundColor - CSS color value (hex, rgb, hsl, CSS variable, etc.)
2166
- * @param {string} lightColor - Light color option (typically for dark backgrounds)
2167
- * @param {string} darkColor - Dark color option (typically for light backgrounds)
2204
+ * @param {string} backgroundColor - CSS color value (hex, rgb, hsl, CSS variable, etc.) to test against
2205
+ * @param {string} [lightColor="white"] - Light color option (typically for dark backgrounds)
2206
+ * @param {string} [darkColor="black"] - Dark color option (typically for light backgrounds)
2207
+ * @param {Element} [element] - DOM element to resolve CSS variables against
2168
2208
  * @returns {string} The color that provides better contrast (lightColor or darkColor)
2209
+ * @example
2210
+ * // Choose text color for a dark blue background
2211
+ * pickLightOrDark("#1a202c") // returns "white"
2212
+ *
2213
+ * // Choose text color for a light background with CSS variable
2214
+ * pickLightOrDark("var(--bg-color)", "white", "black", element) // returns "black" or "white"
2169
2215
  */
2170
2216
 
2171
2217
  const pickLightOrDark = (
2172
- element,
2173
2218
  backgroundColor,
2174
2219
  lightColor = "white",
2175
2220
  darkColor = "black",
2221
+ element,
2176
2222
  ) => {
2177
2223
  const resolvedBgColor = resolveCSSColor(backgroundColor, element);
2178
2224
  const resolvedLightColor = resolveCSSColor(lightColor, element);
@@ -2192,6 +2238,31 @@ const pickLightOrDark = (
2192
2238
  return contrastWithLight > contrastWithDark ? lightColor : darkColor;
2193
2239
  };
2194
2240
 
2241
+ /**
2242
+ * Resolves the luminance value of a CSS color
2243
+ * @param {string} color - CSS color value (hex, rgb, hsl, CSS variable, etc.)
2244
+ * @param {Element} [element] - DOM element to resolve CSS variables against
2245
+ * @returns {number|undefined} Relative luminance (0-1) according to WCAG formula, or undefined if color cannot be resolved
2246
+ * @example
2247
+ * // Get luminance of a hex color
2248
+ * resolveColorLuminance("#ff0000") // returns ~0.213 (red)
2249
+ *
2250
+ * // Get luminance of a CSS variable
2251
+ * resolveColorLuminance("var(--primary-color)", element) // returns luminance value or undefined
2252
+ *
2253
+ * // Use for light/dark classification
2254
+ * const luminance = resolveColorLuminance("#2ecc71");
2255
+ * const isLight = luminance > 0.3; // true for light colors, false for dark
2256
+ */
2257
+ const resolveColorLuminance = (color, element) => {
2258
+ const resolvedColor = resolveCSSColor(color, element);
2259
+ if (!resolvedColor) {
2260
+ return undefined;
2261
+ }
2262
+ const [r, g, b] = resolvedColor;
2263
+ return getLuminance(r, g, b);
2264
+ };
2265
+
2195
2266
  const findAncestor = (node, predicate) => {
2196
2267
  let ancestor = node.parentNode;
2197
2268
  while (ancestor) {
@@ -11803,4 +11874,4 @@ const notifyTransitionOverflow = (element, transitionId) => {
11803
11874
  };
11804
11875
  };
11805
11876
 
11806
- 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, mergeStyles, normalizeStyle, normalizeStyles, parseCSSColor, pickLightOrDark, pickPositionRelativeTo, prefersDarkColors, prefersLightColors, preventFocusNav, preventFocusNavViaKeyboard, resolveCSSColor, resolveCSSSize, setAttribute, setAttributes, setStyles, startDragToResizeGesture, stickyAsRelativeCoords, stringifyCSSColor, trapFocusInside, trapScrollInside, useActiveElement, useAvailableHeight, useAvailableWidth, useMaxHeight, useMaxWidth, useResizeStatus, visibleRectEffect };
11877
+ 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.7.4",
3
+ "version": "0.8.1",
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": "^2.3.2",
40
- "preact": "^10.27.2"
39
+ "@preact/signals": "2.4.0",
40
+ "preact": "11.0.0-beta.0"
41
41
  },
42
42
  "publishConfig": {
43
43
  "access": "public"