@jsenv/plugin-toolbar 0.1.1 → 0.1.2

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 (66) hide show
  1. package/dist/css/document_execution_indicator.css +87 -0
  2. package/dist/css/globals.css +75 -0
  3. package/dist/css/server_indicator.css +83 -0
  4. package/dist/css/theme_light.css +110 -0
  5. package/dist/css/toolbar.css +7 -683
  6. package/dist/css/toolbar_menu_overflow.css +59 -0
  7. package/dist/css/toolbar_settings.css +202 -0
  8. package/dist/css/tooltips.css +67 -0
  9. package/dist/js/animation.js +282 -0
  10. package/dist/js/animation_actions.js +7 -0
  11. package/dist/js/animation_effects.js +10 -0
  12. package/dist/js/animation_signals.js +4 -0
  13. package/dist/js/changes_actions.js +7 -0
  14. package/dist/js/changes_indicator.js +57 -0
  15. package/dist/js/changes_signals.js +2 -0
  16. package/dist/js/document_execution_indicator.js +62 -0
  17. package/dist/js/document_index_link.js +4 -0
  18. package/dist/js/dom.js +48 -0
  19. package/dist/js/execution_actions.js +46 -0
  20. package/dist/js/execution_effects.js +16 -0
  21. package/dist/js/execution_signals.js +17 -0
  22. package/dist/js/hooks.mjs +185 -0
  23. package/dist/js/iframe_to_parent_href.js +10 -0
  24. package/dist/js/notification_actions.js +60 -0
  25. package/dist/js/notification_context.js +1 -0
  26. package/dist/js/notification_effects.js +9 -0
  27. package/dist/js/notification_signals.js +5 -0
  28. package/dist/js/parent_window_actions.js +39 -0
  29. package/dist/js/parent_window_context.js +6 -0
  30. package/dist/js/parent_window_effects.js +15 -0
  31. package/dist/js/parent_window_signals.js +25 -0
  32. package/dist/js/preact.mjs +316 -0
  33. package/dist/js/responsive.js +104 -0
  34. package/dist/js/server_actions.js +7 -0
  35. package/dist/js/server_effects.js +9 -0
  36. package/dist/js/server_indicator.js +52 -0
  37. package/dist/js/server_signals.js +10 -0
  38. package/dist/js/settings_actions.js +7 -0
  39. package/dist/js/settings_signals.js +2 -0
  40. package/dist/js/signals-core.mjs +339 -0
  41. package/dist/js/signals.mjs +202 -0
  42. package/dist/js/theme_actions.js +7 -0
  43. package/dist/js/theme_effects.js +6 -0
  44. package/dist/js/theme_signals.js +4 -0
  45. package/dist/js/toolbar_animation_setting.js +21 -0
  46. package/dist/js/toolbar_autoreload_setting.js +67 -0
  47. package/dist/js/toolbar_close_button.js +14 -0
  48. package/dist/js/toolbar_core.js +8 -1792
  49. package/dist/js/toolbar_injector.js +2 -4
  50. package/dist/js/toolbar_menu_overflow.js +81 -0
  51. package/dist/js/toolbar_notification_setting.js +72 -0
  52. package/dist/js/toolbar_open_actions.js +7 -0
  53. package/dist/js/toolbar_open_effects.js +4 -0
  54. package/dist/js/toolbar_open_signals.js +4 -0
  55. package/dist/js/toolbar_opening.js +65 -0
  56. package/dist/js/toolbar_overlay.js +69 -0
  57. package/dist/js/toolbar_settings.js +44 -0
  58. package/dist/js/toolbar_state_context.js +1 -0
  59. package/dist/js/toolbar_state_effects.js +8 -0
  60. package/dist/js/toolbar_state_signals.js +17 -0
  61. package/dist/js/toolbar_theme_setting.js +13 -0
  62. package/dist/js/toolbar_ui.js +20 -0
  63. package/dist/js/tooltip_actions.js +6 -0
  64. package/dist/js/variant.js +53 -0
  65. package/dist/jsenv_plugin_toolbar.js +3 -6
  66. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  const jsenvLogoSvgUrl = new URL("../other/jsenv_logo.svg", import.meta.url);
2
- const injectToolbar = async ({
2
+ export const injectToolbar = async ({
3
3
  toolbarUrl,
4
4
  logLevel,
5
5
  theme,
@@ -234,6 +234,4 @@ const iframeToLoadedPromise = iframe => {
234
234
  };
235
235
  iframe.addEventListener("load", onload, true);
236
236
  });
237
- };
238
-
239
- export { injectToolbar };
237
+ };
@@ -0,0 +1,81 @@
1
+ import { closeAllTooltips } from "./tooltip_actions.js";
2
+ import { forceHideElement, removeForceHideElement, deactivateToolbarSection } from "./dom.js";
3
+ import { createHorizontalBreakpoint } from "./responsive.js";
4
+ const WINDOW_SMALL_WIDTH = 420;
5
+ export const initToolbarMenuOverflow = () => {
6
+ // apply responsive design on toolbar icons if needed + add listener on resize screen
7
+ // ideally we should listen breakpoint once, for now restore toolbar
8
+ const overflowMenuBreakpoint = createHorizontalBreakpoint(WINDOW_SMALL_WIDTH);
9
+ const handleOverflowMenuBreakpoint = () => {
10
+ responsiveToolbar(overflowMenuBreakpoint);
11
+ };
12
+ handleOverflowMenuBreakpoint();
13
+ overflowMenuBreakpoint.changed.listen(handleOverflowMenuBreakpoint);
14
+ document.querySelector("#menu_overflow_button").onclick = () => {
15
+ if (overflowMenuIsOpened()) {
16
+ closeOverflowMenu();
17
+ } else {
18
+ openOverflowMenu();
19
+ }
20
+ };
21
+ };
22
+ const responsiveToolbar = overflowMenuBreakpoint => {
23
+ // close all tooltips in case opened
24
+ closeAllTooltips();
25
+ // close settings box in case opened
26
+ deactivateToolbarSection(document.querySelector("#settings"));
27
+ if (overflowMenuBreakpoint.isBelow()) {
28
+ enableOverflow();
29
+ } else {
30
+ disableOverflow();
31
+ }
32
+ };
33
+ let moves = [];
34
+ const enableOverflow = () => {
35
+ // move elements from toolbar to overflow menu
36
+ const responsiveToolbarElements = document.querySelectorAll("[data-responsive-toolbar-element]");
37
+ const overflowMenu = document.querySelector("#menu_overflow");
38
+
39
+ // keep a placeholder element to know where to move them back
40
+ moves = Array.from(responsiveToolbarElements).map(element => {
41
+ const placeholder = document.createElement("div");
42
+ placeholder.style.display = "none";
43
+ placeholder.setAttribute("data-placeholder", "");
44
+ element.parentNode.replaceChild(placeholder, element);
45
+ overflowMenu.appendChild(element);
46
+ return {
47
+ element,
48
+ placeholder
49
+ };
50
+ });
51
+ document.querySelector("#toolbar").setAttribute("data-menu-overflow-enabled", "");
52
+ removeForceHideElement(document.querySelector("#menu_overflow_button"));
53
+ };
54
+ const disableOverflow = () => {
55
+ // close overflow menu in case it's open & unselect toggleOverflowMenu button in case it's selected
56
+ closeOverflowMenu();
57
+ deactivateToolbarSection(document.querySelector("#menu_overflow"));
58
+ moves.forEach(({
59
+ element,
60
+ placeholder
61
+ }) => {
62
+ placeholder.parentNode.replaceChild(element, placeholder);
63
+ });
64
+ moves = [];
65
+ document.querySelector("#toolbar").removeAttribute("data-menu-overflow-enabled");
66
+ forceHideElement(document.querySelector("#menu_overflow_button"));
67
+ };
68
+ const overflowMenuIsOpened = () => {
69
+ const toolbar = document.querySelector("#toolbar");
70
+ return toolbar.hasAttribute("data-menu-overflow-opened");
71
+ };
72
+ const openOverflowMenu = () => {
73
+ const toolbar = document.querySelector("#toolbar");
74
+ document.querySelector("#menu_overflow").setAttribute("data-animate", "");
75
+ toolbar.setAttribute("data-menu-overflow-opened", "");
76
+ };
77
+ const closeOverflowMenu = () => {
78
+ const toolbar = document.querySelector("#toolbar");
79
+ toolbar.removeAttribute("data-menu-overflow-opened");
80
+ document.querySelector("#menu_overflow").removeAttribute("data-animate");
81
+ };
@@ -0,0 +1,72 @@
1
+ import { effect } from "./signals.mjs";
2
+ import { notificationsEnabledSignal, notificationPermissionSignal } from "./notification_signals.js";
3
+ import { notificationAPIDetected } from "./notification_context.js";
4
+ import { enableNotifications, disableNotifications, requestPermission } from "./notification_actions.js";
5
+ import { enableVariant } from "./variant.js";
6
+ const notifCheckbox = document.querySelector("#toggle_notifs");
7
+ export const renderToolbarNotificationSetting = () => {
8
+ effect(() => {
9
+ const notificationsEnabled = notificationsEnabledSignal.value;
10
+ notifCheckbox.checked = notificationsEnabled;
11
+ });
12
+ effect(() => {
13
+ const notificationPermission = notificationPermissionSignal.value;
14
+ if (!notificationAPIDetected) {
15
+ applyNotificationNotAvailableEffects();
16
+ return;
17
+ }
18
+ if (notificationPermission === "default") {
19
+ applyNotificationDefaultEffects();
20
+ return;
21
+ }
22
+ if (notificationPermission === "denied") {
23
+ applyNotificationDeniedEffects();
24
+ return;
25
+ }
26
+ if (notificationPermission === "granted") {
27
+ applyNotificationGrantedEffects();
28
+ return;
29
+ }
30
+ });
31
+ };
32
+ const applyNotificationNotAvailableEffects = () => {
33
+ const notifSetting = document.querySelector(".settings_notification");
34
+ notifSetting.setAttribute("data-disabled", "true");
35
+ notifSetting.setAttribute("title", `Notification not available in the browser`);
36
+ notifCheckbox.disabled = true;
37
+ };
38
+ const applyNotificationDefaultEffects = () => {
39
+ applyNotificationNOTGrantedEffects();
40
+ const notifSetting = document.querySelector(".settings_notification");
41
+ notifSetting.removeAttribute("data-disabled");
42
+ notifSetting.removeAttribute("title");
43
+ };
44
+ const applyNotificationDeniedEffects = () => {
45
+ applyNotificationNOTGrantedEffects();
46
+ const notifSetting = document.querySelector(".settings_notification");
47
+ notifSetting.setAttribute("data-disabled", "true");
48
+ notifSetting.setAttribute("title", `Notification denied`);
49
+ };
50
+ const applyNotificationGrantedEffects = () => {
51
+ enableVariant(document.querySelector(".notification_text"), {
52
+ notif_granted: "yes"
53
+ });
54
+ notifCheckbox.disabled = false;
55
+ notifCheckbox.onchange = () => {
56
+ if (notifCheckbox.checked) {
57
+ enableNotifications();
58
+ } else {
59
+ disableNotifications();
60
+ }
61
+ };
62
+ };
63
+ const applyNotificationNOTGrantedEffects = () => {
64
+ enableVariant(document.querySelector(".notification_text"), {
65
+ notif_granted: "no"
66
+ });
67
+ notifCheckbox.disabled = true;
68
+ notifCheckbox.checked = false;
69
+ document.querySelector("a.request_notification_permission").onclick = () => {
70
+ requestPermission();
71
+ };
72
+ };
@@ -0,0 +1,7 @@
1
+ import { openedSignal } from "./toolbar_open_signals.js";
2
+ export const openToolbar = () => {
3
+ openedSignal.value = true;
4
+ };
5
+ export const closeToolbar = () => {
6
+ openedSignal.value = false;
7
+ };
@@ -0,0 +1,4 @@
1
+ import { addExternalCommandCallback } from "./parent_window_actions.js";
2
+ import { openToolbar, closeToolbar } from "./toolbar_open_actions.js";
3
+ addExternalCommandCallback("openToolbar", openToolbar);
4
+ addExternalCommandCallback("closeToolbar", closeToolbar);
@@ -0,0 +1,4 @@
1
+ import { signal } from "./signals.mjs";
2
+ import { paramsFromParentWindow } from "./parent_window_context.js";
3
+ import { stateFromLocalStorage } from "./toolbar_state_context.js";
4
+ export const openedSignal = signal(typeof stateFromLocalStorage.opened === "boolean" ? stateFromLocalStorage.opened : typeof paramsFromParentWindow.opened === "boolean" ? paramsFromParentWindow.opened : false);
@@ -0,0 +1,65 @@
1
+ import { effect } from "./signals.mjs";
2
+ import { closeAllTooltips } from "./tooltip_actions.js";
3
+ import { animationsEnabledSignal } from "./animation_signals.js";
4
+ import { openedSignal } from "./toolbar_open_signals.js";
5
+ import { getToolbarIframe, setStyles } from "./dom.js";
6
+ import { startJavaScriptAnimation } from "./animation.js";
7
+ export const initToolbarOpening = () => {
8
+ effect(() => {
9
+ const opened = openedSignal.value;
10
+ if (opened) {
11
+ showToolbar();
12
+ } else {
13
+ hideToolbar();
14
+ }
15
+ });
16
+ };
17
+ let restoreToolbarIframeParentStyles = () => {};
18
+ let restoreToolbarIframeStyles = () => {};
19
+ const hideToolbar = () => {
20
+ closeAllTooltips();
21
+ restoreToolbarIframeParentStyles();
22
+ restoreToolbarIframeStyles();
23
+ document.documentElement.removeAttribute("data-toolbar-visible");
24
+ };
25
+
26
+ // (by the way it might be cool to have the toolbar auto show when)
27
+ // it has something to say (being disconnected from server)
28
+ const showToolbar = () => {
29
+ const animationsEnabled = animationsEnabledSignal.peek();
30
+ document.documentElement.setAttribute("data-toolbar-visible", "");
31
+ const toolbarIframe = getToolbarIframe();
32
+ const toolbarIframeParent = toolbarIframe.parentNode;
33
+ const parentWindow = window.parent;
34
+ const parentDocumentElement = parentWindow.document.compatMode === "CSS1Compat" ? parentWindow.document.documentElement : parentWindow.document.body;
35
+ const scrollYMax = parentDocumentElement.scrollHeight - parentWindow.innerHeight;
36
+ const scrollY = parentDocumentElement.scrollTop;
37
+ const scrollYRemaining = scrollYMax - scrollY;
38
+ setStyles(toolbarIframeParent, {
39
+ "transition-property": "padding-bottom",
40
+ "transition-duration": animationsEnabled ? "300ms" : "0s"
41
+ });
42
+ // maybe we should use js animation here because we would not conflict with css
43
+ restoreToolbarIframeParentStyles = setStyles(toolbarIframeParent, {
44
+ "scroll-padding-bottom": "40px",
45
+ // same here we should add 40px
46
+ "padding-bottom": "40px" // if there is already one we should add 40px
47
+ });
48
+
49
+ restoreToolbarIframeStyles = setStyles(toolbarIframe, {
50
+ height: "40px",
51
+ visibility: "visible"
52
+ });
53
+ if (scrollYRemaining < 40 && scrollYMax > 0) {
54
+ const scrollEnd = scrollY + 40;
55
+ startJavaScriptAnimation({
56
+ duration: 300,
57
+ onProgress: ({
58
+ progress
59
+ }) => {
60
+ const value = scrollY + (scrollEnd - scrollY) * progress;
61
+ parentDocumentElement.scrollTop = value;
62
+ }
63
+ });
64
+ }
65
+ };
@@ -0,0 +1,69 @@
1
+ import { effect } from "./signals.mjs";
2
+ import { openedSignal } from "./toolbar_open_signals.js";
3
+ import { settingsOpenedSignal } from "./settings_signals.js";
4
+ import { closeSettings } from "./settings_actions.js";
5
+ import { closeAllTooltips } from "./tooltip_actions.js";
6
+ import { executionTooltipOpenedSignal } from "./execution_signals.js";
7
+ import { changesTooltipOpenedSignal } from "./changes_signals.js";
8
+ import { serverTooltipOpenedSignal } from "./server_signals.js";
9
+ import { getToolbarIframe, setStyles } from "./dom.js";
10
+ export const renderToolbarOverlay = () => {
11
+ const toolbarOverlay = document.querySelector("#toolbar_overlay");
12
+ toolbarOverlay.onclick = () => {
13
+ closeAllTooltips();
14
+ closeSettings();
15
+ };
16
+ effect(() => {
17
+ if (!window.parent) {
18
+ // can happen while parent iframe reloads
19
+ return;
20
+ }
21
+ const opened = openedSignal.value;
22
+ const settingsOpened = settingsOpenedSignal.value;
23
+ const executionTooltipOpened = executionTooltipOpenedSignal.value;
24
+ const changesTooltipOpened = changesTooltipOpenedSignal.value;
25
+ const serverTooltipOpened = serverTooltipOpenedSignal.value;
26
+ if (!opened) {
27
+ return;
28
+ }
29
+ if (settingsOpened || executionTooltipOpened || changesTooltipOpened || serverTooltipOpened) {
30
+ enableIframeOverflowOnParentWindow();
31
+ } else {
32
+ disableIframeOverflowOnParentWindow();
33
+ }
34
+ });
35
+ };
36
+ const enableIframeOverflowOnParentWindow = () => {
37
+ const iframe = getToolbarIframe();
38
+ const transitionDuration = iframe.style.transitionDuration;
39
+ setStyles(iframe, {
40
+ "height": "100%",
41
+ // we don't want to animate height transition
42
+ // but if it was enabled, we'll restore it afterwards
43
+ "transition-duration": "0ms"
44
+ });
45
+ if (transitionDuration) {
46
+ setTimeout(() => {
47
+ setStyles(iframe, {
48
+ "transition-duration": transitionDuration
49
+ });
50
+ });
51
+ }
52
+ };
53
+ const disableIframeOverflowOnParentWindow = () => {
54
+ const iframe = getToolbarIframe();
55
+ const transitionDuration = iframe.style.transitionDuration;
56
+ setStyles(iframe, {
57
+ "height": "40px",
58
+ // we don't want to animate height transition
59
+ // but if it was enabled, we'll restore it afterwards
60
+ "transition-duration": "0ms"
61
+ });
62
+ if (transitionDuration) {
63
+ setTimeout(() => {
64
+ setStyles(iframe, {
65
+ "transition-duration": transitionDuration
66
+ });
67
+ });
68
+ }
69
+ };
@@ -0,0 +1,44 @@
1
+ import { effect } from "./signals.mjs";
2
+ import { settingsOpenedSignal } from "./settings_signals.js";
3
+ import { openSettings, closeSettings } from "./settings_actions.js";
4
+ import { enableVariant } from "./variant.js";
5
+ import { activateToolbarSection, deactivateToolbarSection } from "./dom.js";
6
+ import { renderToolbarAutoreloadSetting } from "./toolbar_autoreload_setting.js";
7
+ import { renderToolbarAnimationSetting } from "./toolbar_animation_setting.js";
8
+ import { renderToolbarNotificationSetting } from "./toolbar_notification_setting.js";
9
+ import { renderToolbarThemeSetting } from "./toolbar_theme_setting.js";
10
+ export const renderToolbarSettings = () => {
11
+ document.querySelector("#settings_open_button").onclick = toggleSettings;
12
+ document.querySelector("#settings_close_button").onclick = toggleSettings;
13
+ disableWarningStyle();
14
+ renderToolbarAutoreloadSetting();
15
+ renderToolbarAnimationSetting();
16
+ renderToolbarNotificationSetting();
17
+ renderToolbarThemeSetting();
18
+ effect(() => {
19
+ const settingsOpened = settingsOpenedSignal.value;
20
+ if (settingsOpened) {
21
+ activateToolbarSection(document.querySelector("#settings"));
22
+ } else {
23
+ deactivateToolbarSection(document.querySelector("#settings"));
24
+ }
25
+ });
26
+ };
27
+ const toggleSettings = () => {
28
+ const settingsOpened = settingsOpenedSignal.value;
29
+ if (settingsOpened) {
30
+ closeSettings();
31
+ } else {
32
+ openSettings();
33
+ }
34
+ };
35
+ export const enableWarningStyle = () => {
36
+ enableVariant(document.querySelector("#settings_open_button"), {
37
+ has_warning: "yes"
38
+ });
39
+ };
40
+ export const disableWarningStyle = () => {
41
+ enableVariant(document.querySelector("#settings_open_button"), {
42
+ has_warning: "no"
43
+ });
44
+ };
@@ -0,0 +1 @@
1
+ export const stateFromLocalStorage = localStorage.hasOwnProperty("jsenv_toolbar") ? JSON.parse(localStorage.getItem("jsenv_toolbar")) : {};
@@ -0,0 +1,8 @@
1
+ import { effect } from "./signals.mjs";
2
+ import { toolbarStateSignal } from "./toolbar_state_signals.js";
3
+ import { sendEventToParent } from "./parent_window_actions.js";
4
+ effect(() => {
5
+ const toolbarState = toolbarStateSignal.value;
6
+ localStorage.setItem("jsenv_toolbar", JSON.stringify(toolbarState));
7
+ sendEventToParent("toolbar_state_change", toolbarState);
8
+ });
@@ -0,0 +1,17 @@
1
+ import { computed } from "./signals.mjs";
2
+ import { openedSignal } from "./toolbar_open_signals.js";
3
+ import { themeSignal } from "./theme_signals.js";
4
+ import { animationsEnabledSignal } from "./animation_signals.js";
5
+ import { notificationsEnabledSignal } from "./notification_signals.js";
6
+ export const toolbarStateSignal = computed(() => {
7
+ const opened = openedSignal.value;
8
+ const theme = themeSignal.value;
9
+ const animationsEnabled = animationsEnabledSignal.value;
10
+ const notificationsEnabled = notificationsEnabledSignal.value;
11
+ return {
12
+ opened,
13
+ theme,
14
+ animationsEnabled,
15
+ notificationsEnabled
16
+ };
17
+ });
@@ -0,0 +1,13 @@
1
+ import { themeSignal } from "./theme_signals.js";
2
+ import { switchToDefaultTheme, switchToLightTheme } from "./theme_actions.js";
3
+ export const renderToolbarThemeSetting = () => {
4
+ const checkbox = document.querySelector("#checkbox_dark_theme");
5
+ checkbox.checked = themeSignal.value === "dark";
6
+ checkbox.onchange = () => {
7
+ if (checkbox.checked) {
8
+ switchToDefaultTheme();
9
+ } else {
10
+ switchToLightTheme();
11
+ }
12
+ };
13
+ };
@@ -0,0 +1,20 @@
1
+ import { initToolbarOpening } from "./toolbar_opening.js";
2
+ import { initToolbarMenuOverflow } from "./toolbar_menu_overflow.js";
3
+ import { renderToolbarOverlay } from "./toolbar_overlay.js";
4
+ import { renderDocumentIndexLink } from "./document_index_link.js";
5
+ import { renderDocumentExecutionIndicator } from "./document_execution_indicator.js";
6
+ import { renderChangesIndicator } from "./changes_indicator.js";
7
+ import { renderServerIndicator } from "./server_indicator.js";
8
+ import { renderToolbarSettings } from "./toolbar_settings.js";
9
+ import { renderToolbarCloseButton } from "./toolbar_close_button.js";
10
+ export const initToolbarUI = () => {
11
+ initToolbarOpening();
12
+ initToolbarMenuOverflow();
13
+ renderToolbarOverlay();
14
+ renderDocumentIndexLink();
15
+ renderDocumentExecutionIndicator();
16
+ renderChangesIndicator();
17
+ renderServerIndicator();
18
+ renderToolbarSettings();
19
+ renderToolbarCloseButton();
20
+ };
@@ -0,0 +1,6 @@
1
+ import { closeExecutionTooltip } from "./execution_actions.js";
2
+ import { closeServerTooltip } from "./server_actions.js";
3
+ export const closeAllTooltips = () => {
4
+ closeExecutionTooltip();
5
+ closeServerTooltip();
6
+ };
@@ -0,0 +1,53 @@
1
+ export const enableVariant = (rootNode, variables) => {
2
+ const nodesNotMatching = Array.from(rootNode.querySelectorAll(`[${attributeIndicatingACondition}]`));
3
+ nodesNotMatching.forEach(nodeNotMatching => {
4
+ const conditionAttributeValue = nodeNotMatching.getAttribute(attributeIndicatingACondition);
5
+ const matches = testCondition(conditionAttributeValue, variables);
6
+ if (matches) {
7
+ renameAttribute(nodeNotMatching, attributeIndicatingACondition, attributeIndicatingAMatch);
8
+ }
9
+ });
10
+ const nodesMatching = Array.from(rootNode.querySelectorAll(`[${attributeIndicatingAMatch}]`));
11
+ nodesMatching.forEach(nodeMatching => {
12
+ const conditionAttributeValue = nodeMatching.getAttribute(attributeIndicatingAMatch);
13
+ const matches = testCondition(conditionAttributeValue, variables);
14
+ if (!matches) {
15
+ renameAttribute(nodeMatching, attributeIndicatingAMatch, attributeIndicatingACondition);
16
+ }
17
+ });
18
+ };
19
+ const testCondition = (conditionAttributeValue, variables) => {
20
+ const condition = parseCondition(conditionAttributeValue);
21
+ return Object.keys(variables).some(key => {
22
+ if (condition.key !== key) {
23
+ return false;
24
+ }
25
+ // the condition do not specify a value, any value is ok
26
+ if (condition.value === undefined) {
27
+ return true;
28
+ }
29
+ if (condition.value === variables[key]) {
30
+ return true;
31
+ }
32
+ return false;
33
+ });
34
+ };
35
+ const parseCondition = conditionAttributeValue => {
36
+ const colonIndex = conditionAttributeValue.indexOf(":");
37
+ if (colonIndex === -1) {
38
+ return {
39
+ key: conditionAttributeValue,
40
+ value: undefined
41
+ };
42
+ }
43
+ return {
44
+ key: conditionAttributeValue.slice(0, colonIndex),
45
+ value: conditionAttributeValue.slice(colonIndex + 1)
46
+ };
47
+ };
48
+ const attributeIndicatingACondition = `data-when`;
49
+ const attributeIndicatingAMatch = `data-when-active`;
50
+ const renameAttribute = (node, name, newName) => {
51
+ node.setAttribute(newName, node.getAttribute(name));
52
+ node.removeAttribute(name);
53
+ };
@@ -1,6 +1,5 @@
1
- import { parseHtmlString, injectScriptNodeAsEarlyAsPossible, createHtmlNode, stringifyHtmlAst } from "@jsenv/ast";
2
-
3
- const jsenvPluginToolbar = ({
1
+ import { parseHtmlString, stringifyHtmlAst, injectScriptNodeAsEarlyAsPossible, createHtmlNode } from "@jsenv/ast";
2
+ export const jsenvPluginToolbar = ({
4
3
  logLevel = "warn",
5
4
  theme = "dark",
6
5
  opened = false,
@@ -52,6 +51,4 @@ injectToolbar(${JSON.stringify({
52
51
  }
53
52
  }
54
53
  };
55
- };
56
-
57
- export { jsenvPluginToolbar };
54
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/plugin-toolbar",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -26,7 +26,7 @@
26
26
  "/src/"
27
27
  ],
28
28
  "dependencies": {
29
- "@jsenv/ast": "1.4.8",
29
+ "@jsenv/ast": "1.4.9",
30
30
  "@preact/signals": "1.1.2"
31
31
  },
32
32
  "scripts": {