@jsenv/dom 0.6.1 → 0.7.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 +259 -314
- package/package.json +2 -4
- package/index.js +0 -124
- package/src/attr/add_attribute_effect.js +0 -93
- package/src/attr/attributes.js +0 -32
- package/src/color/color_constrast.js +0 -69
- package/src/color/color_parsing.js +0 -319
- package/src/color/color_scheme.js +0 -28
- package/src/color/pick_light_or_dark.js +0 -34
- package/src/color/resolve_css_color.js +0 -60
- package/src/demos/3_columns_resize_demo.html +0 -84
- package/src/demos/3_rows_resize_demo.html +0 -89
- package/src/demos/aside_and_main_demo.html +0 -93
- package/src/demos/coordinates_demo.html +0 -450
- package/src/demos/document_autoscroll_demo.html +0 -517
- package/src/demos/drag_gesture_constraints_demo.html +0 -701
- package/src/demos/drag_gesture_demo.html +0 -1047
- package/src/demos/drag_gesture_element_to_impact_demo.html +0 -445
- package/src/demos/drag_reference_element_demo.html +0 -480
- package/src/demos/flex_details_set_demo.html +0 -302
- package/src/demos/flex_details_set_demo_2.html +0 -315
- package/src/demos/visible_rect_demo.html +0 -525
- package/src/element_signature.js +0 -100
- package/src/interaction/drag/constraint_feedback_line.js +0 -92
- package/src/interaction/drag/drag_constraint.js +0 -659
- package/src/interaction/drag/drag_debug_markers.js +0 -635
- package/src/interaction/drag/drag_element_positioner.js +0 -382
- package/src/interaction/drag/drag_gesture.js +0 -566
- package/src/interaction/drag/drag_resize_demo.html +0 -571
- package/src/interaction/drag/drag_to_move.js +0 -301
- package/src/interaction/drag/drag_to_resize_gesture.js +0 -68
- package/src/interaction/drag/drop_target_detection.js +0 -148
- package/src/interaction/drag/sticky_frontiers.js +0 -160
- package/src/interaction/event_marker.js +0 -14
- package/src/interaction/focus/active_element.js +0 -33
- package/src/interaction/focus/arrow_navigation.js +0 -599
- package/src/interaction/focus/element_is_focusable.js +0 -57
- package/src/interaction/focus/element_visibility.js +0 -111
- package/src/interaction/focus/find_focusable.js +0 -21
- package/src/interaction/focus/focus_group.js +0 -91
- package/src/interaction/focus/focus_group_registry.js +0 -12
- package/src/interaction/focus/focus_nav.js +0 -12
- package/src/interaction/focus/focus_nav_event_marker.js +0 -14
- package/src/interaction/focus/focus_trap.js +0 -105
- package/src/interaction/focus/tab_navigation.js +0 -128
- package/src/interaction/focus/tests/focus_group_skip_tab_test.html +0 -206
- package/src/interaction/focus/tests/tree_focus_test.html +0 -304
- package/src/interaction/focus/tests/tree_focus_test.jsx +0 -261
- package/src/interaction/focus/tests/tree_focus_test_preact.html +0 -13
- package/src/interaction/isolate_interactions.js +0 -161
- package/src/interaction/keyboard.js +0 -26
- package/src/interaction/scroll/capture_scroll.js +0 -47
- package/src/interaction/scroll/is_scrollable.js +0 -159
- package/src/interaction/scroll/scroll_container.js +0 -110
- package/src/interaction/scroll/scroll_trap.js +0 -44
- package/src/interaction/scroll/scrollbar_size.js +0 -20
- package/src/interaction/scroll/wheel_through.js +0 -138
- package/src/iterable_weak_set.js +0 -66
- package/src/position/dom_coords.js +0 -340
- package/src/position/offset_parent.js +0 -15
- package/src/position/position_fixed.js +0 -15
- package/src/position/position_sticky.js +0 -213
- package/src/position/sticky_rect.js +0 -79
- package/src/position/visible_rect.js +0 -486
- package/src/pub_sub.js +0 -31
- package/src/size/can_take_size.js +0 -11
- package/src/size/details_content_full_height.js +0 -63
- package/src/size/flex_details_set.js +0 -974
- package/src/size/get_available_height.js +0 -22
- package/src/size/get_available_width.js +0 -22
- package/src/size/get_border_sizes.js +0 -14
- package/src/size/get_height.js +0 -4
- package/src/size/get_inner_height.js +0 -15
- package/src/size/get_inner_width.js +0 -15
- package/src/size/get_margin_sizes.js +0 -10
- package/src/size/get_max_height.js +0 -57
- package/src/size/get_max_width.js +0 -47
- package/src/size/get_min_height.js +0 -14
- package/src/size/get_min_width.js +0 -14
- package/src/size/get_padding_sizes.js +0 -10
- package/src/size/get_width.js +0 -4
- package/src/size/hooks/use_available_height.js +0 -27
- package/src/size/hooks/use_available_width.js +0 -27
- package/src/size/hooks/use_max_height.js +0 -10
- package/src/size/hooks/use_max_width.js +0 -10
- package/src/size/hooks/use_resize_status.js +0 -62
- package/src/size/resize.js +0 -695
- package/src/size/resolve_css_size.js +0 -32
- package/src/style/dom_styles.js +0 -97
- package/src/style/style_composition.js +0 -121
- package/src/style/style_controller.js +0 -345
- package/src/style/style_default.js +0 -153
- package/src/style/style_default_demo.html +0 -128
- package/src/style/style_parsing.js +0 -375
- package/src/transition/demos/animation_resumption_test.xhtml +0 -500
- package/src/transition/demos/height_toggle_test.xhtml +0 -515
- package/src/transition/dom_transition.js +0 -254
- package/src/transition/easing.js +0 -48
- package/src/transition/group_transition.js +0 -261
- package/src/transition/transform_style_parser.js +0 -32
- package/src/transition/transition_playback.js +0 -366
- package/src/transition/transition_timeline.js +0 -79
- package/src/traversal.js +0 -247
- package/src/ui_transition/demos/content_states_transition_demo.html +0 -628
- package/src/ui_transition/demos/smooth_height_transition_demo.html +0 -149
- package/src/ui_transition/demos/transition_testing.html +0 -354
- package/src/ui_transition/ui_transition.js +0 -1470
- package/src/utils.js +0 -69
- package/src/value_effect.js +0 -35
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/dom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "DOM utilities for writing frontend code",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -26,9 +26,7 @@
|
|
|
26
26
|
"./resize": "./src/size/resize.js"
|
|
27
27
|
},
|
|
28
28
|
"files": [
|
|
29
|
-
"/dist/"
|
|
30
|
-
"/src/",
|
|
31
|
-
"/index.js"
|
|
29
|
+
"/dist/"
|
|
32
30
|
],
|
|
33
31
|
"scripts": {
|
|
34
32
|
"build": "node ./scripts/build.mjs",
|
package/index.js
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
export { getElementSignature } from "./src/element_signature.js";
|
|
2
|
-
|
|
3
|
-
// state management
|
|
4
|
-
export { createIterableWeakSet } from "./src/iterable_weak_set.js";
|
|
5
|
-
export { createPubSub } from "./src/pub_sub.js";
|
|
6
|
-
export { createValueEffect } from "./src/value_effect.js";
|
|
7
|
-
|
|
8
|
-
// style
|
|
9
|
-
export { addWillChange, getStyle, setStyles } from "./src/style/dom_styles.js";
|
|
10
|
-
export { appendStyles, mergeStyles } from "./src/style/style_composition.js";
|
|
11
|
-
export { createStyleController } from "./src/style/style_controller.js";
|
|
12
|
-
export { getDefaultStyles } from "./src/style/style_default.js";
|
|
13
|
-
export { normalizeStyles } from "./src/style/style_parsing.js";
|
|
14
|
-
|
|
15
|
-
// attributes
|
|
16
|
-
export { addAttributeEffect } from "./src/attr/add_attribute_effect.js";
|
|
17
|
-
export { setAttribute, setAttributes } from "./src/attr/attributes.js";
|
|
18
|
-
|
|
19
|
-
// colors
|
|
20
|
-
export { getContrastRatio } from "./src/color/color_constrast.js";
|
|
21
|
-
export { parseCSSColor, stringifyCSSColor } from "./src/color/color_parsing.js";
|
|
22
|
-
export {
|
|
23
|
-
getPreferedColorScheme,
|
|
24
|
-
prefersDarkColors,
|
|
25
|
-
prefersLightColors,
|
|
26
|
-
} from "./src/color/color_scheme.js";
|
|
27
|
-
export { pickLightOrDark } from "./src/color/pick_light_or_dark.js";
|
|
28
|
-
export { resolveCSSColor } from "./src/color/resolve_css_color.js";
|
|
29
|
-
|
|
30
|
-
// traversal
|
|
31
|
-
export {
|
|
32
|
-
findAfter,
|
|
33
|
-
findAncestor,
|
|
34
|
-
findBefore,
|
|
35
|
-
findDescendant,
|
|
36
|
-
} from "./src/traversal.js";
|
|
37
|
-
|
|
38
|
-
// interaction/focus
|
|
39
|
-
export {
|
|
40
|
-
activeElementSignal,
|
|
41
|
-
addActiveElementEffect,
|
|
42
|
-
useActiveElement,
|
|
43
|
-
} from "./src/interaction/focus/active_element.js";
|
|
44
|
-
export { elementIsFocusable } from "./src/interaction/focus/element_is_focusable.js";
|
|
45
|
-
export {
|
|
46
|
-
elementIsVisibleForFocus,
|
|
47
|
-
elementIsVisuallyVisible,
|
|
48
|
-
getFirstVisuallyVisibleAncestor,
|
|
49
|
-
getFocusVisibilityInfo,
|
|
50
|
-
getVisuallyVisibleInfo,
|
|
51
|
-
} from "./src/interaction/focus/element_visibility.js";
|
|
52
|
-
export { findFocusable } from "./src/interaction/focus/find_focusable.js";
|
|
53
|
-
export { initFocusGroup } from "./src/interaction/focus/focus_group.js";
|
|
54
|
-
export { preventFocusNavViaKeyboard } from "./src/interaction/focus/focus_nav.js";
|
|
55
|
-
export { preventFocusNav } from "./src/interaction/focus/focus_nav_event_marker.js";
|
|
56
|
-
export { trapFocusInside } from "./src/interaction/focus/focus_trap.js";
|
|
57
|
-
// interaction/keyboard
|
|
58
|
-
export { canInterceptKeys } from "./src/interaction/keyboard.js";
|
|
59
|
-
// interaction/scroll
|
|
60
|
-
export { captureScrollState } from "./src/interaction/scroll/capture_scroll.js";
|
|
61
|
-
export { isScrollable } from "./src/interaction/scroll/is_scrollable.js";
|
|
62
|
-
export {
|
|
63
|
-
getScrollContainer,
|
|
64
|
-
getScrollContainerSet,
|
|
65
|
-
getSelfAndAncestorScrolls,
|
|
66
|
-
} from "./src/interaction/scroll/scroll_container.js";
|
|
67
|
-
export { trapScrollInside } from "./src/interaction/scroll/scroll_trap.js";
|
|
68
|
-
export { allowWheelThrough } from "./src/interaction/scroll/wheel_through.js";
|
|
69
|
-
// interaction/drag
|
|
70
|
-
export { getDragCoordinates } from "./src/interaction/drag/drag_element_positioner.js";
|
|
71
|
-
export {
|
|
72
|
-
createDragGestureController,
|
|
73
|
-
dragAfterThreshold,
|
|
74
|
-
} from "./src/interaction/drag/drag_gesture.js";
|
|
75
|
-
export { createDragToMoveGestureController } from "./src/interaction/drag/drag_to_move.js";
|
|
76
|
-
export { startDragToResizeGesture } from "./src/interaction/drag/drag_to_resize_gesture.js";
|
|
77
|
-
export { getDropTargetInfo } from "./src/interaction/drag/drop_target_detection.js";
|
|
78
|
-
|
|
79
|
-
// position
|
|
80
|
-
export { getScrollRelativeRect } from "./src/position/dom_coords.js";
|
|
81
|
-
export { getPositionedParent } from "./src/position/offset_parent.js";
|
|
82
|
-
export { initPositionSticky } from "./src/position/position_sticky.js";
|
|
83
|
-
export { stickyAsRelativeCoords } from "./src/position/sticky_rect.js";
|
|
84
|
-
export {
|
|
85
|
-
pickPositionRelativeTo,
|
|
86
|
-
visibleRectEffect,
|
|
87
|
-
} from "./src/position/visible_rect.js";
|
|
88
|
-
|
|
89
|
-
// size
|
|
90
|
-
export { initFlexDetailsSet } from "./src/size/flex_details_set.js";
|
|
91
|
-
export { getAvailableHeight } from "./src/size/get_available_height.js";
|
|
92
|
-
export { getAvailableWidth } from "./src/size/get_available_width.js";
|
|
93
|
-
export { getBorderSizes } from "./src/size/get_border_sizes.js";
|
|
94
|
-
export { getHeight } from "./src/size/get_height.js";
|
|
95
|
-
export { getInnerHeight } from "./src/size/get_inner_height.js";
|
|
96
|
-
export { getInnerWidth } from "./src/size/get_inner_width.js";
|
|
97
|
-
export { getMarginSizes } from "./src/size/get_margin_sizes.js";
|
|
98
|
-
export { getMaxHeight } from "./src/size/get_max_height.js";
|
|
99
|
-
export { getMaxWidth } from "./src/size/get_max_width.js";
|
|
100
|
-
export { getMinHeight } from "./src/size/get_min_height.js";
|
|
101
|
-
export { getMinWidth } from "./src/size/get_min_width.js";
|
|
102
|
-
export { getPaddingSizes } from "./src/size/get_padding_sizes.js";
|
|
103
|
-
export { getWidth } from "./src/size/get_width.js";
|
|
104
|
-
export { resolveCSSSize } from "./src/size/resolve_css_size.js";
|
|
105
|
-
// size hooks
|
|
106
|
-
export { useAvailableHeight } from "./src/size/hooks/use_available_height.js";
|
|
107
|
-
export { useAvailableWidth } from "./src/size/hooks/use_available_width.js";
|
|
108
|
-
export { useMaxHeight } from "./src/size/hooks/use_max_height.js";
|
|
109
|
-
export { useMaxWidth } from "./src/size/hooks/use_max_width.js";
|
|
110
|
-
export { useResizeStatus } from "./src/size/hooks/use_resize_status.js";
|
|
111
|
-
|
|
112
|
-
// transition
|
|
113
|
-
export {
|
|
114
|
-
createHeightTransition,
|
|
115
|
-
createOpacityTransition,
|
|
116
|
-
createTranslateXTransition,
|
|
117
|
-
createWidthTransition,
|
|
118
|
-
} from "./src/transition/dom_transition.js";
|
|
119
|
-
export { EASING, cubicBezier } from "./src/transition/easing.js";
|
|
120
|
-
export {
|
|
121
|
-
createTimelineTransition,
|
|
122
|
-
createTransition,
|
|
123
|
-
} from "./src/transition/transition_playback.js";
|
|
124
|
-
export { initUITransition } from "./src/ui_transition/ui_transition.js";
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
export const addAttributeEffect = (attributeName, effect) => {
|
|
2
|
-
const cleanupWeakMap = new WeakMap();
|
|
3
|
-
const applyEffect = (element) => {
|
|
4
|
-
const cleanup = effect(element);
|
|
5
|
-
cleanupWeakMap.set(
|
|
6
|
-
element,
|
|
7
|
-
typeof cleanup === "function" ? cleanup : () => {},
|
|
8
|
-
);
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
const cleanupEffect = (element) => {
|
|
12
|
-
const cleanup = cleanupWeakMap.get(element);
|
|
13
|
-
if (cleanup) {
|
|
14
|
-
cleanup();
|
|
15
|
-
cleanupWeakMap.delete(element);
|
|
16
|
-
}
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const checkElement = (element) => {
|
|
20
|
-
if (element.hasAttribute(attributeName)) {
|
|
21
|
-
applyEffect(element);
|
|
22
|
-
}
|
|
23
|
-
const elementWithAttributeCollection = element.querySelectorAll(
|
|
24
|
-
`[${attributeName}]`,
|
|
25
|
-
);
|
|
26
|
-
for (const elementWithAttribute of elementWithAttributeCollection) {
|
|
27
|
-
applyEffect(elementWithAttribute);
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
checkElement(document.body);
|
|
32
|
-
const mutationObserver = new MutationObserver((mutations) => {
|
|
33
|
-
for (const mutation of mutations) {
|
|
34
|
-
if (mutation.type === "childList") {
|
|
35
|
-
for (const addedNode of mutation.addedNodes) {
|
|
36
|
-
if (addedNode.nodeType !== Node.ELEMENT_NODE) {
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
checkElement(addedNode);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
for (const removedNode of mutation.removedNodes) {
|
|
43
|
-
if (removedNode.nodeType !== Node.ELEMENT_NODE) {
|
|
44
|
-
continue;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Clean up the removed node itself if it had the attribute
|
|
48
|
-
if (
|
|
49
|
-
removedNode.hasAttribute &&
|
|
50
|
-
removedNode.hasAttribute(attributeName)
|
|
51
|
-
) {
|
|
52
|
-
cleanupEffect(removedNode);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// Clean up any children of the removed node that had the attribute
|
|
56
|
-
if (removedNode.querySelectorAll) {
|
|
57
|
-
const elementsWithAttribute = removedNode.querySelectorAll(
|
|
58
|
-
`[${attributeName}]`,
|
|
59
|
-
);
|
|
60
|
-
for (const element of elementsWithAttribute) {
|
|
61
|
-
cleanupEffect(element);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
if (
|
|
67
|
-
mutation.type === "attributes" &&
|
|
68
|
-
mutation.attributeName === attributeName
|
|
69
|
-
) {
|
|
70
|
-
const element = mutation.target;
|
|
71
|
-
if (element.hasAttribute(attributeName)) {
|
|
72
|
-
applyEffect(element);
|
|
73
|
-
} else {
|
|
74
|
-
cleanupEffect(element);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
mutationObserver.observe(document.body, {
|
|
80
|
-
childList: true,
|
|
81
|
-
subtree: true,
|
|
82
|
-
attributes: true,
|
|
83
|
-
attributeFilter: [attributeName],
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
return () => {
|
|
87
|
-
mutationObserver.disconnect();
|
|
88
|
-
for (const cleanup of cleanupWeakMap.values()) {
|
|
89
|
-
cleanup();
|
|
90
|
-
}
|
|
91
|
-
cleanupWeakMap.clear();
|
|
92
|
-
};
|
|
93
|
-
};
|
package/src/attr/attributes.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
export const setAttribute = (element, name, value) => {
|
|
2
|
-
if (element.hasAttribute(name)) {
|
|
3
|
-
const prevValue = element.getAttribute(name);
|
|
4
|
-
element.setAttribute(name, value);
|
|
5
|
-
return () => {
|
|
6
|
-
element.setAttribute(name, prevValue);
|
|
7
|
-
};
|
|
8
|
-
}
|
|
9
|
-
element.setAttribute(name, value);
|
|
10
|
-
return () => {
|
|
11
|
-
element.removeAttribute(name);
|
|
12
|
-
};
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const createSetMany = (setter) => {
|
|
16
|
-
return (element, description) => {
|
|
17
|
-
const cleanupCallbackSet = new Set();
|
|
18
|
-
for (const name of Object.keys(description)) {
|
|
19
|
-
const value = description[name];
|
|
20
|
-
const restoreStyle = setter(element, name, value);
|
|
21
|
-
cleanupCallbackSet.add(restoreStyle);
|
|
22
|
-
}
|
|
23
|
-
return () => {
|
|
24
|
-
for (const cleanupCallback of cleanupCallbackSet) {
|
|
25
|
-
cleanupCallback();
|
|
26
|
-
}
|
|
27
|
-
cleanupCallbackSet.clear();
|
|
28
|
-
};
|
|
29
|
-
};
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export const setAttributes = createSetMany(setAttribute);
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Calculates the contrast ratio between two RGBA colors
|
|
3
|
-
* Based on WCAG 2.1 specification
|
|
4
|
-
* @param {Array<number>} rgba1 - [r, g, b, a] values for first color
|
|
5
|
-
* @param {Array<number>} rgba2 - [r, g, b, a] values for second color
|
|
6
|
-
* @param {Array<number>} [background=[255, 255, 255, 1]] - Background color to composite against when colors have transparency
|
|
7
|
-
* @returns {number} Contrast ratio (1-21)
|
|
8
|
-
*/
|
|
9
|
-
export const getContrastRatio = (
|
|
10
|
-
rgba1,
|
|
11
|
-
rgba2,
|
|
12
|
-
background = [255, 255, 255, 1],
|
|
13
|
-
) => {
|
|
14
|
-
// When colors have transparency (alpha < 1), we need to composite them
|
|
15
|
-
// against a background to get their effective appearance
|
|
16
|
-
const composited1 = compositeColor(rgba1, background);
|
|
17
|
-
const composited2 = compositeColor(rgba2, background);
|
|
18
|
-
|
|
19
|
-
const lum1 = getLuminance(composited1[0], composited1[1], composited1[2]);
|
|
20
|
-
const lum2 = getLuminance(composited2[0], composited2[1], composited2[2]);
|
|
21
|
-
const brightest = Math.max(lum1, lum2);
|
|
22
|
-
const darkest = Math.min(lum1, lum2);
|
|
23
|
-
return (brightest + 0.05) / (darkest + 0.05);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Composites a color with alpha over a background color
|
|
28
|
-
* @param {Array<number>} foreground - [r, g, b, a] foreground color
|
|
29
|
-
* @param {Array<number>} background - [r, g, b, a] background color
|
|
30
|
-
* @returns {Array<number>} [r, g, b] composited color (alpha is flattened)
|
|
31
|
-
*/
|
|
32
|
-
const compositeColor = (foreground, background) => {
|
|
33
|
-
const [fr, fg, fb, fa] = foreground;
|
|
34
|
-
const [br, bg, bb, ba] = background;
|
|
35
|
-
|
|
36
|
-
// No transparency: return the foreground color as-is
|
|
37
|
-
if (fa === 1) {
|
|
38
|
-
return [fr, fg, fb];
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Alpha compositing formula: C = αA * CA + αB * (1 - αA) * CB
|
|
42
|
-
const alpha = fa + ba * (1 - fa);
|
|
43
|
-
|
|
44
|
-
if (alpha === 0) {
|
|
45
|
-
return [0, 0, 0];
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const r = (fa * fr + ba * (1 - fa) * br) / alpha;
|
|
49
|
-
const g = (fa * fg + ba * (1 - fa) * bg) / alpha;
|
|
50
|
-
const b = (fa * fb + ba * (1 - fa) * bb) / alpha;
|
|
51
|
-
|
|
52
|
-
return [Math.round(r), Math.round(g), Math.round(b)];
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Calculates the relative luminance of an RGB color
|
|
57
|
-
* Based on WCAG 2.1 specification
|
|
58
|
-
* @param {number} r - Red component (0-255)
|
|
59
|
-
* @param {number} g - Green component (0-255)
|
|
60
|
-
* @param {number} b - Blue component (0-255)
|
|
61
|
-
* @returns {number} Relative luminance (0-1)
|
|
62
|
-
*/
|
|
63
|
-
const getLuminance = (r, g, b) => {
|
|
64
|
-
const [rs, gs, bs] = [r, g, b].map((c) => {
|
|
65
|
-
c = c / 255;
|
|
66
|
-
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
|
|
67
|
-
});
|
|
68
|
-
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
|
|
69
|
-
};
|
|
@@ -1,319 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parses a CSS color string into RGBA values
|
|
3
|
-
* Supports hex (#rgb, #rrggbb, #rrggbbaa), rgb(), rgba(), hsl(), hsla()
|
|
4
|
-
* @param {string} color - CSS color string
|
|
5
|
-
* @returns {Array<number>|null} [r, g, b, a] values or null if parsing fails
|
|
6
|
-
*/
|
|
7
|
-
export const parseCSSColor = (color) => {
|
|
8
|
-
if (!color || typeof color !== "string") {
|
|
9
|
-
return null;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
color = color.trim().toLowerCase();
|
|
13
|
-
|
|
14
|
-
// Hex colors
|
|
15
|
-
if (color.startsWith("#")) {
|
|
16
|
-
const hex = color.slice(1);
|
|
17
|
-
if (hex.length === 3) {
|
|
18
|
-
// #rgb -> #rrggbb
|
|
19
|
-
const r = parseInt(hex[0] + hex[0], 16);
|
|
20
|
-
const g = parseInt(hex[1] + hex[1], 16);
|
|
21
|
-
const b = parseInt(hex[2] + hex[2], 16);
|
|
22
|
-
return [r, g, b, 1];
|
|
23
|
-
}
|
|
24
|
-
if (hex.length === 6) {
|
|
25
|
-
// #rrggbb
|
|
26
|
-
const r = parseInt(hex.slice(0, 2), 16);
|
|
27
|
-
const g = parseInt(hex.slice(2, 4), 16);
|
|
28
|
-
const b = parseInt(hex.slice(4, 6), 16);
|
|
29
|
-
return [r, g, b, 1];
|
|
30
|
-
}
|
|
31
|
-
if (hex.length === 8) {
|
|
32
|
-
// #rrggbbaa
|
|
33
|
-
const r = parseInt(hex.slice(0, 2), 16);
|
|
34
|
-
const g = parseInt(hex.slice(2, 4), 16);
|
|
35
|
-
const b = parseInt(hex.slice(4, 6), 16);
|
|
36
|
-
const a = parseInt(hex.slice(6, 8), 16) / 255;
|
|
37
|
-
return [r, g, b, a];
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// RGB/RGBA colors
|
|
42
|
-
const rgbMatch = color.match(/rgba?\(([^)]+)\)/);
|
|
43
|
-
if (rgbMatch) {
|
|
44
|
-
const values = rgbMatch[1].split(",").map((v) => parseFloat(v.trim()));
|
|
45
|
-
if (values.length >= 3) {
|
|
46
|
-
const r = values[0];
|
|
47
|
-
const g = values[1];
|
|
48
|
-
const b = values[2];
|
|
49
|
-
const a = values.length >= 4 ? values[3] : 1;
|
|
50
|
-
return [r, g, b, a];
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// HSL/HSLA colors - convert to RGB
|
|
55
|
-
const hslMatch = color.match(/hsla?\(([^)]+)\)/);
|
|
56
|
-
if (hslMatch) {
|
|
57
|
-
const values = hslMatch[1].split(",").map((v) => parseFloat(v.trim()));
|
|
58
|
-
if (values.length >= 3) {
|
|
59
|
-
const [h, s, l] = values;
|
|
60
|
-
const a = values.length >= 4 ? values[3] : 1;
|
|
61
|
-
const [r, g, b] = hslToRgb(h, s / 100, l / 100);
|
|
62
|
-
return [r, g, b, a];
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Named colors (basic set)
|
|
67
|
-
if (namedColors[color]) {
|
|
68
|
-
return [...namedColors[color], 1];
|
|
69
|
-
}
|
|
70
|
-
return null;
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Converts RGBA values back to a CSS color string
|
|
75
|
-
* Prefers named colors when possible, then rgb() for opaque colors, rgba() for transparent
|
|
76
|
-
* @param {Array<number>} rgba - [r, g, b, a] values
|
|
77
|
-
* @returns {string|null} CSS color string or null if invalid input
|
|
78
|
-
*/
|
|
79
|
-
export const stringifyCSSColor = (rgba) => {
|
|
80
|
-
if (!Array.isArray(rgba) || rgba.length < 3) {
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const [r, g, b, a = 1] = rgba;
|
|
85
|
-
|
|
86
|
-
// Validate RGB values
|
|
87
|
-
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Validate alpha value
|
|
92
|
-
if (a < 0 || a > 1) {
|
|
93
|
-
return null;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Round RGB values to integers
|
|
97
|
-
const rInt = Math.round(r);
|
|
98
|
-
const gInt = Math.round(g);
|
|
99
|
-
const bInt = Math.round(b);
|
|
100
|
-
|
|
101
|
-
// Check for named colors (only for fully opaque colors)
|
|
102
|
-
if (a === 1) {
|
|
103
|
-
for (const [name, [nameR, nameG, nameB]] of Object.entries(namedColors)) {
|
|
104
|
-
if (rInt === nameR && gInt === nameG && bInt === nameB) {
|
|
105
|
-
return name;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Use rgb() for opaque colors, rgba() for transparent
|
|
111
|
-
if (a === 1) {
|
|
112
|
-
return `rgb(${rInt}, ${gInt}, ${bInt})`;
|
|
113
|
-
}
|
|
114
|
-
return `rgba(${rInt}, ${gInt}, ${bInt}, ${a})`;
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
const namedColors = {
|
|
118
|
-
// Basic colors
|
|
119
|
-
black: [0, 0, 0],
|
|
120
|
-
white: [255, 255, 255],
|
|
121
|
-
red: [255, 0, 0],
|
|
122
|
-
green: [0, 128, 0],
|
|
123
|
-
blue: [0, 0, 255],
|
|
124
|
-
yellow: [255, 255, 0],
|
|
125
|
-
cyan: [0, 255, 255],
|
|
126
|
-
magenta: [255, 0, 255],
|
|
127
|
-
|
|
128
|
-
// Gray variations
|
|
129
|
-
silver: [192, 192, 192],
|
|
130
|
-
gray: [128, 128, 128],
|
|
131
|
-
grey: [128, 128, 128],
|
|
132
|
-
darkgray: [169, 169, 169],
|
|
133
|
-
darkgrey: [169, 169, 169],
|
|
134
|
-
lightgray: [211, 211, 211],
|
|
135
|
-
lightgrey: [211, 211, 211],
|
|
136
|
-
dimgray: [105, 105, 105],
|
|
137
|
-
dimgrey: [105, 105, 105],
|
|
138
|
-
gainsboro: [220, 220, 220],
|
|
139
|
-
whitesmoke: [245, 245, 245],
|
|
140
|
-
|
|
141
|
-
// Extended basic colors
|
|
142
|
-
maroon: [128, 0, 0],
|
|
143
|
-
olive: [128, 128, 0],
|
|
144
|
-
lime: [0, 255, 0],
|
|
145
|
-
aqua: [0, 255, 255],
|
|
146
|
-
teal: [0, 128, 128],
|
|
147
|
-
navy: [0, 0, 128],
|
|
148
|
-
fuchsia: [255, 0, 255],
|
|
149
|
-
purple: [128, 0, 128],
|
|
150
|
-
|
|
151
|
-
// Red variations
|
|
152
|
-
darkred: [139, 0, 0],
|
|
153
|
-
firebrick: [178, 34, 34],
|
|
154
|
-
crimson: [220, 20, 60],
|
|
155
|
-
indianred: [205, 92, 92],
|
|
156
|
-
lightcoral: [240, 128, 128],
|
|
157
|
-
salmon: [250, 128, 114],
|
|
158
|
-
darksalmon: [233, 150, 122],
|
|
159
|
-
lightsalmon: [255, 160, 122],
|
|
160
|
-
|
|
161
|
-
// Pink variations
|
|
162
|
-
pink: [255, 192, 203],
|
|
163
|
-
lightpink: [255, 182, 193],
|
|
164
|
-
hotpink: [255, 105, 180],
|
|
165
|
-
deeppink: [255, 20, 147],
|
|
166
|
-
mediumvioletred: [199, 21, 133],
|
|
167
|
-
palevioletred: [219, 112, 147],
|
|
168
|
-
|
|
169
|
-
// Orange variations
|
|
170
|
-
orange: [255, 165, 0],
|
|
171
|
-
darkorange: [255, 140, 0],
|
|
172
|
-
orangered: [255, 69, 0],
|
|
173
|
-
tomato: [255, 99, 71],
|
|
174
|
-
coral: [255, 127, 80],
|
|
175
|
-
|
|
176
|
-
// Yellow variations
|
|
177
|
-
gold: [255, 215, 0],
|
|
178
|
-
lightyellow: [255, 255, 224],
|
|
179
|
-
lemonchiffon: [255, 250, 205],
|
|
180
|
-
lightgoldenrodyellow: [250, 250, 210],
|
|
181
|
-
papayawhip: [255, 239, 213],
|
|
182
|
-
moccasin: [255, 228, 181],
|
|
183
|
-
peachpuff: [255, 218, 185],
|
|
184
|
-
palegoldenrod: [238, 232, 170],
|
|
185
|
-
khaki: [240, 230, 140],
|
|
186
|
-
darkkhaki: [189, 183, 107],
|
|
187
|
-
|
|
188
|
-
// Green variations
|
|
189
|
-
darkgreen: [0, 100, 0],
|
|
190
|
-
forestgreen: [34, 139, 34],
|
|
191
|
-
seagreen: [46, 139, 87],
|
|
192
|
-
mediumseagreen: [60, 179, 113],
|
|
193
|
-
springgreen: [0, 255, 127],
|
|
194
|
-
mediumspringgreen: [0, 250, 154],
|
|
195
|
-
lawngreen: [124, 252, 0],
|
|
196
|
-
chartreuse: [127, 255, 0],
|
|
197
|
-
greenyellow: [173, 255, 47],
|
|
198
|
-
limegreen: [50, 205, 50],
|
|
199
|
-
palegreen: [152, 251, 152],
|
|
200
|
-
lightgreen: [144, 238, 144],
|
|
201
|
-
mediumaquamarine: [102, 205, 170],
|
|
202
|
-
aquamarine: [127, 255, 212],
|
|
203
|
-
darkolivegreen: [85, 107, 47],
|
|
204
|
-
olivedrab: [107, 142, 35],
|
|
205
|
-
yellowgreen: [154, 205, 50],
|
|
206
|
-
|
|
207
|
-
// Blue variations
|
|
208
|
-
darkblue: [0, 0, 139],
|
|
209
|
-
mediumblue: [0, 0, 205],
|
|
210
|
-
royalblue: [65, 105, 225],
|
|
211
|
-
steelblue: [70, 130, 180],
|
|
212
|
-
dodgerblue: [30, 144, 255],
|
|
213
|
-
deepskyblue: [0, 191, 255],
|
|
214
|
-
skyblue: [135, 206, 235],
|
|
215
|
-
lightskyblue: [135, 206, 250],
|
|
216
|
-
lightblue: [173, 216, 230],
|
|
217
|
-
powderblue: [176, 224, 230],
|
|
218
|
-
lightcyan: [224, 255, 255],
|
|
219
|
-
paleturquoise: [175, 238, 238],
|
|
220
|
-
darkturquoise: [0, 206, 209],
|
|
221
|
-
mediumturquoise: [72, 209, 204],
|
|
222
|
-
turquoise: [64, 224, 208],
|
|
223
|
-
cadetblue: [95, 158, 160],
|
|
224
|
-
darkcyan: [0, 139, 139],
|
|
225
|
-
lightseagreen: [32, 178, 170],
|
|
226
|
-
|
|
227
|
-
// Purple variations
|
|
228
|
-
indigo: [75, 0, 130],
|
|
229
|
-
darkviolet: [148, 0, 211],
|
|
230
|
-
blueviolet: [138, 43, 226],
|
|
231
|
-
mediumpurple: [147, 112, 219],
|
|
232
|
-
mediumslateblue: [123, 104, 238],
|
|
233
|
-
slateblue: [106, 90, 205],
|
|
234
|
-
darkslateblue: [72, 61, 139],
|
|
235
|
-
lavender: [230, 230, 250],
|
|
236
|
-
thistle: [216, 191, 216],
|
|
237
|
-
plum: [221, 160, 221],
|
|
238
|
-
violet: [238, 130, 238],
|
|
239
|
-
orchid: [218, 112, 214],
|
|
240
|
-
mediumorchid: [186, 85, 211],
|
|
241
|
-
darkorchid: [153, 50, 204],
|
|
242
|
-
darkmagenta: [139, 0, 139],
|
|
243
|
-
|
|
244
|
-
// Brown variations
|
|
245
|
-
brown: [165, 42, 42],
|
|
246
|
-
saddlebrown: [139, 69, 19],
|
|
247
|
-
sienna: [160, 82, 45],
|
|
248
|
-
chocolate: [210, 105, 30],
|
|
249
|
-
darkgoldenrod: [184, 134, 11],
|
|
250
|
-
peru: [205, 133, 63],
|
|
251
|
-
rosybrown: [188, 143, 143],
|
|
252
|
-
goldenrod: [218, 165, 32],
|
|
253
|
-
sandybrown: [244, 164, 96],
|
|
254
|
-
tan: [210, 180, 140],
|
|
255
|
-
burlywood: [222, 184, 135],
|
|
256
|
-
wheat: [245, 222, 179],
|
|
257
|
-
navajowhite: [255, 222, 173],
|
|
258
|
-
bisque: [255, 228, 196],
|
|
259
|
-
blanchedalmond: [255, 235, 205],
|
|
260
|
-
cornsilk: [255, 248, 220],
|
|
261
|
-
|
|
262
|
-
// Special colors
|
|
263
|
-
transparent: [0, 0, 0], // Note: alpha will be 0 for transparent
|
|
264
|
-
aliceblue: [240, 248, 255],
|
|
265
|
-
antiquewhite: [250, 235, 215],
|
|
266
|
-
azure: [240, 255, 255],
|
|
267
|
-
beige: [245, 245, 220],
|
|
268
|
-
honeydew: [240, 255, 240],
|
|
269
|
-
ivory: [255, 255, 240],
|
|
270
|
-
lavenderblush: [255, 240, 245],
|
|
271
|
-
linen: [250, 240, 230],
|
|
272
|
-
mintcream: [245, 255, 250],
|
|
273
|
-
mistyrose: [255, 228, 225],
|
|
274
|
-
oldlace: [253, 245, 230],
|
|
275
|
-
seashell: [255, 245, 238],
|
|
276
|
-
snow: [255, 250, 250],
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
/**
|
|
280
|
-
* Converts HSL color to RGB
|
|
281
|
-
* @param {number} h - Hue (0-360)
|
|
282
|
-
* @param {number} s - Saturation (0-1)
|
|
283
|
-
* @param {number} l - Lightness (0-1)
|
|
284
|
-
* @returns {Array<number>} [r, g, b] values
|
|
285
|
-
*/
|
|
286
|
-
const hslToRgb = (h, s, l) => {
|
|
287
|
-
h = h % 360;
|
|
288
|
-
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
289
|
-
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
|
|
290
|
-
const m = l - c / 2;
|
|
291
|
-
const createRgb = (r, g, b) => {
|
|
292
|
-
return [
|
|
293
|
-
Math.round((r + m) * 255),
|
|
294
|
-
Math.round((g + m) * 255),
|
|
295
|
-
Math.round((b + m) * 255),
|
|
296
|
-
];
|
|
297
|
-
};
|
|
298
|
-
|
|
299
|
-
if (h >= 0 && h < 60) {
|
|
300
|
-
return createRgb(c, x, 0);
|
|
301
|
-
}
|
|
302
|
-
if (h >= 60 && h < 120) {
|
|
303
|
-
return createRgb(x, c, 0);
|
|
304
|
-
}
|
|
305
|
-
if (h >= 120 && h < 180) {
|
|
306
|
-
return createRgb(0, c, x);
|
|
307
|
-
}
|
|
308
|
-
if (h >= 180 && h < 240) {
|
|
309
|
-
return createRgb(0, x, c);
|
|
310
|
-
}
|
|
311
|
-
if (h >= 240 && h < 300) {
|
|
312
|
-
return createRgb(x, 0, c);
|
|
313
|
-
}
|
|
314
|
-
if (h >= 300 && h < 360) {
|
|
315
|
-
return createRgb(c, 0, x);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
return createRgb(0, 0, 0);
|
|
319
|
-
};
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Determines if the current color scheme is dark mode
|
|
3
|
-
* @param {Element} [element] - DOM element to check color-scheme against (optional)
|
|
4
|
-
* @returns {boolean} True if dark mode is active
|
|
5
|
-
*/
|
|
6
|
-
export const prefersDarkColors = (element) => {
|
|
7
|
-
const colorScheme = getPreferedColorScheme(element);
|
|
8
|
-
return colorScheme.includes("dark");
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export const prefersLightColors = (element) => {
|
|
12
|
-
return !prefersDarkColors(element);
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export const getPreferedColorScheme = (element) => {
|
|
16
|
-
const computedStyle = getComputedStyle(element || document.documentElement);
|
|
17
|
-
const colorScheme = computedStyle.colorScheme;
|
|
18
|
-
|
|
19
|
-
// If no explicit color-scheme is set, or it's "normal",
|
|
20
|
-
// fall back to prefers-color-scheme media query
|
|
21
|
-
if (!colorScheme || colorScheme === "normal") {
|
|
22
|
-
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
|
23
|
-
? "dark"
|
|
24
|
-
: "light";
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return colorScheme;
|
|
28
|
-
};
|