@jsenv/navi 0.0.1 → 0.1.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.
Files changed (138) hide show
  1. package/dist/jsenv_navi.js +22954 -0
  2. package/index.js +66 -16
  3. package/package.json +22 -11
  4. package/src/actions.js +50 -26
  5. package/src/browser_integration/browser_integration.js +31 -6
  6. package/src/browser_integration/via_history.js +42 -9
  7. package/src/components/action_execution/render_actionable_component.jsx +6 -4
  8. package/src/components/action_execution/use_action.js +51 -282
  9. package/src/components/action_execution/use_execute_action.js +106 -92
  10. package/src/components/action_execution/use_run_on_mount.js +9 -0
  11. package/src/components/action_renderer.jsx +21 -32
  12. package/src/components/demos/0_button_demo.html +574 -103
  13. package/src/components/demos/10_column_reordering_debug.html +277 -0
  14. package/src/components/demos/11_table_selection_debug.html +432 -0
  15. package/src/components/demos/1_checkbox_demo.html +579 -202
  16. package/src/components/demos/2_input_textual_demo.html +81 -138
  17. package/src/components/demos/3_radio_demo.html +0 -2
  18. package/src/components/demos/4_select_demo.html +19 -23
  19. package/src/components/demos/6_tablist_demo.html +77 -0
  20. package/src/components/demos/7_table_selection_demo.html +176 -0
  21. package/src/components/demos/8_table_fixed_headers_demo.html +584 -0
  22. package/src/components/demos/9_table_column_drag_demo.html +325 -0
  23. package/src/components/demos/action/0_button_demo.html +2 -4
  24. package/src/components/demos/action/1_input_text_demo.html +643 -222
  25. package/src/components/demos/action/3_details_demo.html +146 -115
  26. package/src/components/demos/action/4_input_checkbox_demo.html +442 -322
  27. package/src/components/demos/action/5_input_checkbox_state_demo.html +270 -0
  28. package/src/components/demos/action/6_checkbox_list_demo.html +304 -72
  29. package/src/components/demos/action/7_radio_list_demo.html +310 -170
  30. package/src/components/demos/action/{8_editable_text_demo.html → 8_editable_demo.html} +65 -76
  31. package/src/components/demos/action/9_link_demo.html +84 -62
  32. package/src/components/demos/ui_transition/0_action_renderer_ui_transition_demo.html +695 -0
  33. package/src/components/demos/ui_transition/1_nested_ui_transition_demo.html +429 -0
  34. package/src/components/demos/ui_transition/2_height_transition_test.html +295 -0
  35. package/src/components/details/details.jsx +62 -64
  36. package/src/components/edition/editable.jsx +186 -0
  37. package/src/components/field/README.md +247 -0
  38. package/src/components/{input → field}/button.jsx +151 -130
  39. package/src/components/field/checkbox_list.jsx +184 -0
  40. package/src/components/{collect_form_element_values.js → field/collect_form_element_values.js} +7 -4
  41. package/src/components/{input → field}/field_css.js +4 -1
  42. package/src/components/field/form.jsx +211 -0
  43. package/src/components/{input → field}/input.jsx +1 -0
  44. package/src/components/{input → field}/input_checkbox.jsx +132 -155
  45. package/src/components/{input → field}/input_radio.jsx +135 -46
  46. package/src/components/{input → field}/input_textual.jsx +247 -173
  47. package/src/components/field/label.jsx +32 -0
  48. package/src/components/field/radio_list.jsx +182 -0
  49. package/src/components/{input → field}/select.jsx +17 -32
  50. package/src/components/field/use_action_events.js +132 -0
  51. package/src/components/field/use_form_events.js +55 -0
  52. package/src/components/field/use_ui_state_controller.js +506 -0
  53. package/src/components/item_tracker/README.md +461 -0
  54. package/src/components/item_tracker/use_isolated_item_tracker.jsx +209 -0
  55. package/src/components/item_tracker/use_isolated_item_tracker_demo.html +148 -0
  56. package/src/components/item_tracker/use_isolated_item_tracker_demo.jsx +460 -0
  57. package/src/components/item_tracker/use_item_tracker.jsx +143 -0
  58. package/src/components/item_tracker/use_item_tracker_demo.html +207 -0
  59. package/src/components/item_tracker/use_item_tracker_demo.jsx +216 -0
  60. package/src/components/keyboard_shortcuts/active_keyboard_shortcuts.jsx +87 -0
  61. package/src/components/keyboard_shortcuts/aria_key_shortcuts.js +61 -0
  62. package/src/components/keyboard_shortcuts/keyboard_key_meta.js +17 -0
  63. package/src/components/keyboard_shortcuts/keyboard_shortcuts.js +371 -0
  64. package/src/components/link/link.jsx +65 -102
  65. package/src/components/link/link_with_icon.jsx +52 -0
  66. package/src/components/loader/loader_background.jsx +85 -64
  67. package/src/components/loader/rectangle_loading.jsx +38 -19
  68. package/src/components/route.jsx +8 -4
  69. package/src/components/selection/selection.jsx +1583 -0
  70. package/src/components/svg/font_sized_svg.jsx +45 -0
  71. package/src/components/svg/icon_and_text.jsx +21 -0
  72. package/src/components/svg/svg_mask_overlay.jsx +105 -0
  73. package/src/components/table/drag/table_drag.jsx +506 -0
  74. package/src/components/table/resize/table_resize.jsx +650 -0
  75. package/src/components/table/resize/table_size.js +43 -0
  76. package/src/components/table/selection/table_selection.js +106 -0
  77. package/src/components/table/selection/table_selection.jsx +203 -0
  78. package/src/components/table/sticky/sticky_group.js +354 -0
  79. package/src/components/table/sticky/table_sticky.js +25 -0
  80. package/src/components/table/sticky/table_sticky.jsx +501 -0
  81. package/src/components/table/table.jsx +721 -0
  82. package/src/components/table/table_css.js +211 -0
  83. package/src/components/table/table_ui.jsx +49 -0
  84. package/src/components/table/use_cells_and_columns.js +90 -0
  85. package/src/components/table/use_object_array_to_cells.js +46 -0
  86. package/src/components/table/z_indexes.js +23 -0
  87. package/src/components/tablist/tablist.jsx +99 -0
  88. package/src/components/text/overflow.jsx +15 -0
  89. package/src/components/text/text_and_count.jsx +28 -0
  90. package/src/components/ui_transition.jsx +128 -0
  91. package/src/components/use_auto_focus.js +58 -7
  92. package/src/components/use_batch_during_render.js +33 -0
  93. package/src/components/use_debounce_true.js +7 -7
  94. package/src/components/use_dependencies_diff.js +35 -0
  95. package/src/components/use_focus_group.js +4 -3
  96. package/src/components/use_initial_value.js +8 -34
  97. package/src/components/use_signal_sync.js +1 -1
  98. package/src/components/use_stable_callback.js +68 -0
  99. package/src/components/use_state_array.js +16 -9
  100. package/src/docs/actions.md +22 -0
  101. package/src/notes.md +33 -12
  102. package/src/route/route.js +97 -47
  103. package/src/store/resource_graph.js +2 -1
  104. package/src/store/tests/{resource_graph_dependencies.test.js → resource_graph_dependencies.test_manual.js} +13 -13
  105. package/src/utils/is_signal.js +20 -0
  106. package/src/utils/stringify_for_display.js +4 -23
  107. package/src/validation/constraints/confirm_constraint.js +14 -0
  108. package/src/validation/constraints/create_unique_value_constraint.js +27 -0
  109. package/src/validation/constraints/native_constraints.js +313 -0
  110. package/src/validation/constraints/readonly_constraint.js +36 -0
  111. package/src/validation/constraints/single_space_constraint.js +13 -0
  112. package/src/validation/custom_constraint_validation.js +599 -0
  113. package/src/validation/custom_message.js +18 -0
  114. package/src/validation/demos/browser_style.png +0 -0
  115. package/src/validation/demos/form_validation_demo.html +142 -0
  116. package/src/validation/demos/form_validation_demo_preact.html +87 -0
  117. package/src/validation/demos/form_validation_native_popover_demo.html +168 -0
  118. package/src/validation/demos/form_validation_vs_native_demo.html +172 -0
  119. package/src/validation/demos/validation_message_demo.html +203 -0
  120. package/src/validation/hooks/use_constraints.js +23 -0
  121. package/src/validation/hooks/use_custom_validation_ref.js +73 -0
  122. package/src/validation/hooks/use_validation_message.js +19 -0
  123. package/src/validation/validation_message.js +741 -0
  124. package/src/components/editable_text/editable_text.jsx +0 -96
  125. package/src/components/form.jsx +0 -144
  126. package/src/components/input/checkbox_list.jsx +0 -294
  127. package/src/components/input/field.jsx +0 -61
  128. package/src/components/input/radio_list.jsx +0 -283
  129. package/src/components/input/use_form_event.js +0 -20
  130. package/src/components/input/use_on_change.js +0 -12
  131. package/src/components/selection/selection.js +0 -5
  132. package/src/components/selection/selection_context.jsx +0 -262
  133. package/src/components/shortcut/shortcut_context.jsx +0 -390
  134. package/src/components/use_action_events.js +0 -37
  135. package/src/utils/iterable_weak_set.js +0 -62
  136. /package/src/components/demos/action/{11_nested_shortcuts_demo.html → 11_nested_shortcuts_demo.xhtml} +0 -0
  137. /package/src/components/{shortcut → keyboard_shortcuts}/os.js +0 -0
  138. /package/src/route/{route.test.html → route.xtest.html} +0 -0
package/index.js CHANGED
@@ -1,13 +1,12 @@
1
1
  // actions
2
2
  export { createAction, rerunActions, updateActions } from "./src/actions.js";
3
- export {
4
- ShortcutProvider,
5
- useKeyboardShortcuts,
6
- } from "./src/components/shortcut/shortcut_context.jsx";
3
+ export { useRunOnMount } from "./src/components/action_execution/use_run_on_mount.js";
4
+ export { useKeyboardShortcuts } from "./src/components/keyboard_shortcuts/keyboard_shortcuts.js";
7
5
  export { useActionData } from "./src/use_action_data.js";
8
6
  export { useActionStatus } from "./src/use_action_status.js";
9
7
 
10
8
  // state management (store)
9
+ export { useStateArray } from "./src/components/use_state_array.js";
11
10
  export { resource } from "./src/store/resource_graph.js";
12
11
  export { valueInLocalStorage } from "./src/store/value_in_local_storage.js";
13
12
 
@@ -30,22 +29,73 @@ export { ActionRenderer } from "./src/components/action_renderer.jsx";
30
29
  export { Details } from "./src/components/details/details.jsx";
31
30
  export { SummaryMarker } from "./src/components/details/summary_marker.jsx";
32
31
  export {
33
- EditableText,
34
- useEditableController,
35
- } from "./src/components/editable_text/editable_text.jsx";
32
+ Editable,
33
+ useEditionController,
34
+ } from "./src/components/edition/editable.jsx";
36
35
  export { ErrorBoundaryContext } from "./src/components/error_boundary_context.js";
37
- export { Form } from "./src/components/form.jsx";
38
- export { Button } from "./src/components/input/button.jsx";
39
- export { CheckboxList } from "./src/components/input/checkbox_list.jsx";
40
- export { Field } from "./src/components/input/field.jsx";
41
- export { Input } from "./src/components/input/input.jsx";
42
- export { RadioList } from "./src/components/input/radio_list.jsx";
43
- export { Select } from "./src/components/input/select.jsx";
36
+ export { Button } from "./src/components/field/button.jsx";
37
+ export {
38
+ Checkbox,
39
+ CheckboxList,
40
+ } from "./src/components/field/checkbox_list.jsx";
41
+ export { Form } from "./src/components/field/form.jsx";
42
+ export { Input } from "./src/components/field/input.jsx";
43
+ export { Label } from "./src/components/field/label.jsx";
44
+ export { Radio, RadioList } from "./src/components/field/radio_list.jsx";
45
+ export { Select } from "./src/components/field/select.jsx";
46
+ export { ActiveKeyboardShortcuts } from "./src/components/keyboard_shortcuts/active_keyboard_shortcuts.jsx";
44
47
  export { Link } from "./src/components/link/link.jsx";
45
48
  export { Route } from "./src/components/route.jsx";
46
- export { SelectionProvider } from "./src/components/selection/selection.js";
49
+ export {
50
+ SelectionContext,
51
+ createSelectionKeyboardShortcuts,
52
+ useSelectableElement,
53
+ useSelectionController,
54
+ } from "./src/components/selection/selection.jsx";
55
+ // Table start
56
+ export {
57
+ isCellSelected,
58
+ isColumnSelected,
59
+ isRowSelected,
60
+ stringifyTableSelectionValue,
61
+ } from "./src/components/table/selection/table_selection.js";
62
+ export {
63
+ Col,
64
+ Colgroup,
65
+ RowNumberCol,
66
+ RowNumberTableCell,
67
+ Table,
68
+ TableCell,
69
+ Tbody,
70
+ Thead,
71
+ Tr,
72
+ } from "./src/components/table/table.jsx";
73
+ export { useCellsAndColumns } from "./src/components/table/use_cells_and_columns.js";
74
+ // Table end
75
+ export { Tab, TabList } from "./src/components/tablist/tablist.jsx";
76
+ export { UITransition } from "./src/components/ui_transition.jsx";
47
77
  export { useSignalSync } from "./src/components/use_signal_sync.js";
48
78
 
49
- // for debbugging testing purposes
79
+ // Text and icons
80
+ export { LinkWithIcon } from "./src/components/link/link_with_icon.jsx";
81
+ export { FontSizedSvg } from "./src/components/svg/font_sized_svg.jsx";
82
+ export { IconAndText } from "./src/components/svg/icon_and_text.jsx";
83
+ export { SVGMaskOverlay } from "./src/components/svg/svg_mask_overlay.jsx";
84
+ export { Overflow } from "./src/components/text/overflow.jsx";
85
+ export { TextAndCount } from "./src/components/text/text_and_count.jsx";
86
+
87
+ // Validation
88
+ export { createUniqueValueConstraint } from "./src/validation/constraints/create_unique_value_constraint.js";
89
+ export { SINGLE_SPACE_CONSTRAINT } from "./src/validation/constraints/single_space_constraint.js";
90
+ export {
91
+ addCustomMessage,
92
+ removeCustomMessage,
93
+ } from "./src/validation/custom_message.js";
94
+
95
+ // Other
96
+ export { useDependenciesDiff } from "./src/components/use_dependencies_diff.js";
97
+ export { useFocusGroup } from "./src/components/use_focus_group.js";
98
+
99
+ // for debugging testing purposes
50
100
  export { enableDebugActions } from "./src/actions.js";
51
101
  export { enableDebugOnDocumentLoading } from "./src/browser_integration/document_loading_signal.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jsenv/navi",
3
- "version": "0.0.1",
4
- "description": "A navigation library for JS environments",
3
+ "version": "0.1.0",
4
+ "description": "Library of components including navigation to create frontend applications",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/jsenv/core",
@@ -12,25 +12,36 @@
12
12
  "name": "dmail",
13
13
  "email": "dmaillard06@gmail.com"
14
14
  },
15
- "sideEffects": false,
15
+ "sideEffects": [
16
+ "./dist/jsenv_navi.js"
17
+ ],
16
18
  "type": "module",
17
19
  "exports": {
18
- ".": "./index.js",
20
+ ".": {
21
+ "dev:jsenv": "./index.js",
22
+ "default": "./dist/jsenv_navi.js"
23
+ },
19
24
  "./*": "./*"
20
25
  },
21
26
  "files": [
22
- "/index.js",
23
- "/src/"
27
+ "/dist/",
28
+ "/src/",
29
+ "/index.js"
24
30
  ],
31
+ "scripts": {
32
+ "build": "node ./scripts/build.mjs",
33
+ "prepublishOnly": "npm run build"
34
+ },
25
35
  "dependencies": {
26
- "@jsenv/dom": "0.0.1",
36
+ "@jsenv/dom": "0.1.0",
27
37
  "@jsenv/humanize": "1.6.0"
28
38
  },
29
39
  "devDependencies": {
30
- "@jsenv/assert": "4.5.3",
31
- "@jsenv/validation": "0.0.1",
32
- "@preact/signals": "2.2.0",
33
- "preact": "10.26.9"
40
+ "@jsenv/assert": "4.5.4",
41
+ "@jsenv/core": "../../../",
42
+ "@jsenv/plugin-preact": "../../related/plugin-preact",
43
+ "@preact/signals": "2.3.2",
44
+ "preact": "10.27.2"
34
45
  },
35
46
  "publishConfig": {
36
47
  "access": "public"
package/src/actions.js CHANGED
@@ -1,5 +1,7 @@
1
+ import { createIterableWeakSet } from "@jsenv/dom";
1
2
  import { prefixFirstAndIndentRemainingLines } from "@jsenv/humanize";
2
3
  import { batch, computed, effect, signal } from "@preact/signals";
4
+
3
5
  import {
4
6
  getActionPrivateProperties,
5
7
  setActionPrivateProperties,
@@ -12,21 +14,19 @@ import {
12
14
  RUNNING,
13
15
  } from "./action_run_states.js";
14
16
  import { SYMBOL_OBJECT_SIGNAL } from "./symbol_object_signal.js";
15
- import { createIterableWeakSet } from "./utils/iterable_weak_set.js";
17
+ import { isSignal } from "./utils/is_signal.js";
16
18
  import { createJsValueWeakMap } from "./utils/js_value_weak_map.js";
17
19
  import { mergeTwoJsValues } from "./utils/merge_two_js_values.js";
18
- import {
19
- isSignal,
20
- stringifyForDisplay,
21
- } from "./utils/stringify_for_display.js";
20
+ import { stringifyForDisplay } from "./utils/stringify_for_display.js";
22
21
  import { weakEffect } from "./utils/weak_effect.js";
23
22
 
24
- const ACTION_AS_FUNCTION = true;
25
23
  let DEBUG = false;
26
24
  export const enableDebugActions = () => {
27
25
  DEBUG = true;
28
26
  };
29
27
 
28
+ const ACTION_AS_FUNCTION = true;
29
+
30
30
  let dispatchActions = (params) => {
31
31
  const { requestedResult } = updateActions({
32
32
  globalAbortSignal: new AbortController().signal,
@@ -297,7 +297,11 @@ export const updateActions = ({
297
297
  }
298
298
 
299
299
  if (DEBUG) {
300
- console.group(`updateActions()`);
300
+ let argSource = `reason: \`${reason}\``;
301
+ if (isReplace) {
302
+ argSource += `, isReplace: true`;
303
+ }
304
+ console.group(`updateActions({ ${argSource} })`);
301
305
  const lines = [
302
306
  ...(prerunSet.size ? [formatActionSet(prerunSet, "- prerun:")] : []),
303
307
  ...(runSet.size ? [formatActionSet(runSet, "- run:")] : []),
@@ -306,8 +310,7 @@ export const updateActions = ({
306
310
  ];
307
311
  console.debug(
308
312
  `requested operations:
309
- ${lines.join("\n")}
310
- - meta: { reason: ${reason}, isReplace: ${isReplace} }`,
313
+ ${lines.join("\n")}`,
311
314
  );
312
315
  }
313
316
 
@@ -557,7 +560,6 @@ ${lines.join("\n")}`);
557
560
 
558
561
  const NO_PARAMS = {};
559
562
  const initialParamsDefault = NO_PARAMS;
560
- const metaDefault = {};
561
563
 
562
564
  const actionWeakMap = new WeakMap();
563
565
  export const createAction = (callback, rootOptions = {}) => {
@@ -568,10 +570,10 @@ export const createAction = (callback, rootOptions = {}) => {
568
570
 
569
571
  let rootAction;
570
572
 
571
- const createActionCore = (
572
- {
573
+ const createActionCore = (options, { parentAction } = {}) => {
574
+ let {
573
575
  name = callback.name || "anonymous",
574
- params = initialParamsDefault,
576
+ params,
575
577
  isPrerun = true,
576
578
  runningState = IDLE,
577
579
  aborted = false,
@@ -583,12 +585,15 @@ export const createAction = (callback, rootOptions = {}) => {
583
585
  renderLoadedAsync,
584
586
  sideEffect = () => {},
585
587
  keepOldData = false,
586
- meta = metaDefault,
588
+ meta = {},
587
589
  dataEffect,
588
590
  completeSideEffect,
589
- },
590
- { parentAction } = {},
591
- ) => {
591
+ } = options;
592
+ if (!Object.hasOwn(options, "params")) {
593
+ // even undefined should be respect it's only when not provided at all we use default
594
+ params = initialParamsDefault;
595
+ }
596
+
592
597
  const initialData = data;
593
598
  const paramsSignal = signal(params);
594
599
  const isPrerunSignal = signal(isPrerun);
@@ -660,7 +665,8 @@ export const createAction = (callback, rootOptions = {}) => {
660
665
  if (isSignal(newParamsOrSignal)) {
661
666
  const combinedParamsSignal = computed(() => {
662
667
  const newParams = newParamsOrSignal.value;
663
- return mergeTwoJsValues(params, newParams);
668
+ const result = mergeTwoJsValues(params, newParams);
669
+ return result;
664
670
  });
665
671
  return createActionProxyFromSignal(
666
672
  action,
@@ -692,12 +698,15 @@ export const createAction = (callback, rootOptions = {}) => {
692
698
  if (signalMap.size === 0) {
693
699
  // Pas de signals, merge statique normal
694
700
  if (params === null || typeof params !== "object") {
695
- return createChildAction(newParamsOrSignal, options);
701
+ return createChildAction({
702
+ ...options,
703
+ params: newParamsOrSignal,
704
+ });
696
705
  }
697
706
  const combinedParams = mergeTwoJsValues(params, newParamsOrSignal);
698
707
  return createChildAction({
699
- params: combinedParams,
700
708
  ...options,
709
+ params: combinedParams,
701
710
  });
702
711
  }
703
712
 
@@ -979,7 +988,7 @@ export const createAction = (callback, rootOptions = {}) => {
979
988
  completeSideEffect?.(action);
980
989
  });
981
990
  if (DEBUG) {
982
- console.log(`"${action}": completed (reason: ${reason})`);
991
+ console.log(`"${action}": completed`);
983
992
  }
984
993
  return computedDataSignal.peek();
985
994
  };
@@ -1196,12 +1205,27 @@ const createActionProxyFromSignal = (
1196
1205
  };
1197
1206
 
1198
1207
  const nameSignal = signal();
1199
- const actionProxy = {
1208
+ let actionProxy;
1209
+ if (ACTION_AS_FUNCTION) {
1210
+ actionProxy = function actionProxyFunction() {
1211
+ return actionProxy.rerun();
1212
+ };
1213
+ Object.defineProperty(actionProxy, "name", {
1214
+ configurable: true,
1215
+ get() {
1216
+ return nameSignal.value;
1217
+ },
1218
+ });
1219
+ } else {
1220
+ actionProxy = {
1221
+ get name() {
1222
+ return nameSignal.value;
1223
+ },
1224
+ };
1225
+ }
1226
+ Object.assign(actionProxy, {
1200
1227
  isProxy: true,
1201
1228
  callback: undefined,
1202
- get name() {
1203
- return nameSignal.value;
1204
- },
1205
1229
  params: undefined,
1206
1230
  isPrerun: undefined,
1207
1231
  runningState: undefined,
@@ -1228,7 +1252,7 @@ const createActionProxyFromSignal = (
1228
1252
  replaceParams: null, // Will be set below
1229
1253
  toString: () => actionProxy.name,
1230
1254
  meta: {},
1231
- };
1255
+ });
1232
1256
  Object.preventExtensions(actionProxy);
1233
1257
 
1234
1258
  onActionTargetChange((actionTarget) => {
@@ -26,7 +26,14 @@ const applyActions = (params) => {
26
26
 
27
27
  const applyRouting = (
28
28
  url,
29
- { globalAbortSignal, abortSignal, state, replace, isVisited },
29
+ {
30
+ globalAbortSignal,
31
+ abortSignal,
32
+ // state
33
+ replace,
34
+ isVisited,
35
+ reason,
36
+ },
30
37
  ) => {
31
38
  const {
32
39
  loadSet,
@@ -34,7 +41,11 @@ const applyRouting = (
34
41
  abortSignalMap,
35
42
  routeLoadRequestedMap,
36
43
  activeRouteSet,
37
- } = updateRoutes(url, { state, replace, isVisited });
44
+ } = updateRoutes(url, {
45
+ replace,
46
+ // state,
47
+ isVisited,
48
+ });
38
49
  if (loadSet.size === 0 && reloadSet.size === 0) {
39
50
  return {
40
51
  allResult: undefined,
@@ -48,7 +59,7 @@ const applyRouting = (
48
59
  runSet: loadSet,
49
60
  rerunSet: reloadSet,
50
61
  abortSignalMap,
51
- reason: `Document navigating to ${url}`,
62
+ reason,
52
63
  });
53
64
  const { allResult, runningActionSet } = updateActionsResult;
54
65
  const pendingTaskNameArray = [];
@@ -163,12 +174,26 @@ const useNavStateBasic = (id, initialValue, { debug } = {}) => {
163
174
  }
164
175
 
165
176
  const currentState = browserIntegration.getDocumentState() || {};
177
+
166
178
  if (value === undefined) {
179
+ if (!Object.hasOwn(currentState, id)) {
180
+ return;
181
+ }
167
182
  delete currentState[id];
168
- } else {
169
- currentState[id] = value;
183
+ browserIntegration.replaceDocumentState(currentState, {
184
+ reason: `delete "${id}" from browser state`,
185
+ });
186
+ return;
187
+ }
188
+
189
+ const valueInBrowserState = currentState[id];
190
+ if (valueInBrowserState === value) {
191
+ return;
170
192
  }
171
- browserIntegration.replaceDocumentState(currentState);
193
+ currentState[id] = value;
194
+ browserIntegration.replaceDocumentState(currentState, {
195
+ reason: `set { ${id}: ${value} } in browser state`,
196
+ });
172
197
  };
173
198
 
174
199
  return [
@@ -30,10 +30,17 @@ export const setupBrowserIntegrationViaHistory = ({
30
30
  return window.history.state ? { ...window.history.state } : null;
31
31
  };
32
32
 
33
- const replaceDocumentState = (newState) => {
33
+ const replaceDocumentState = (
34
+ newState,
35
+ { reason = "replaceDocumentState called" } = {},
36
+ ) => {
34
37
  const url = window.location.href;
35
38
  window.history.replaceState(newState, null, url);
36
- handleRoutingTask(url, { state: newState });
39
+ handleRoutingTask(url, {
40
+ replace: true,
41
+ state: newState,
42
+ reason,
43
+ });
37
44
  };
38
45
 
39
46
  const historyStartAtStart = getDocumentState();
@@ -73,7 +80,7 @@ export const setupBrowserIntegrationViaHistory = ({
73
80
  };
74
81
 
75
82
  let abortController = null;
76
- const handleRoutingTask = (url, { state, replace }) => {
83
+ const handleRoutingTask = (url, { state, replace, reason }) => {
77
84
  markUrlAsVisited(url);
78
85
  updateDocumentUrl(url);
79
86
  updateDocumentState(state);
@@ -88,6 +95,7 @@ export const setupBrowserIntegrationViaHistory = ({
88
95
  state,
89
96
  replace,
90
97
  isVisited,
98
+ reason,
91
99
  });
92
100
 
93
101
  executeWithCleanup(
@@ -122,11 +130,23 @@ export const setupBrowserIntegrationViaHistory = ({
122
130
  if (linkElement.hasAttribute("data-readonly")) {
123
131
  return;
124
132
  }
125
- // TODO: ignore anchor navigation
133
+ // Ignore anchor navigation (same page, different hash)
134
+ const currentUrl = new URL(window.location.href);
135
+ const targetUrl = new URL(href);
136
+ if (
137
+ currentUrl.pathname === targetUrl.pathname &&
138
+ currentUrl.search === targetUrl.search &&
139
+ targetUrl.hash !== ""
140
+ ) {
141
+ return;
142
+ }
126
143
  e.preventDefault();
127
144
  const state = null;
128
145
  history.pushState(state, null, href);
129
- handleRoutingTask(href, { state });
146
+ handleRoutingTask(href, {
147
+ state,
148
+ reason: `"click" on a[href="${href}"]`,
149
+ });
130
150
  },
131
151
  { capture: true },
132
152
  );
@@ -142,7 +162,10 @@ export const setupBrowserIntegrationViaHistory = ({
142
162
  window.addEventListener("popstate", (popstateEvent) => {
143
163
  const url = window.location.href;
144
164
  const state = popstateEvent.state;
145
- handleRoutingTask(url, { state });
165
+ handleRoutingTask(url, {
166
+ state,
167
+ reason: `"popstate" event for ${url}`,
168
+ });
146
169
  });
147
170
 
148
171
  const goTo = async (url, { state = null, replace } = {}) => {
@@ -155,7 +178,11 @@ export const setupBrowserIntegrationViaHistory = ({
155
178
  } else {
156
179
  window.history.pushState(state, null, url);
157
180
  }
158
- handleRoutingTask(url, { state, replace });
181
+ handleRoutingTask(url, {
182
+ state,
183
+ replace,
184
+ reason: `goTo called with "${url}"`,
185
+ });
159
186
  };
160
187
 
161
188
  const stop = (reason = "stop called") => {
@@ -165,7 +192,9 @@ export const setupBrowserIntegrationViaHistory = ({
165
192
  const reload = () => {
166
193
  const url = window.location.href;
167
194
  const state = history.state;
168
- handleRoutingTask(url, { state });
195
+ handleRoutingTask(url, {
196
+ state,
197
+ });
169
198
  };
170
199
 
171
200
  const goBack = () => {
@@ -180,7 +209,11 @@ export const setupBrowserIntegrationViaHistory = ({
180
209
  const url = window.location.href;
181
210
  const state = history.state;
182
211
  history.replaceState(state, null, url);
183
- handleRoutingTask(url, { state, replace: true });
212
+ handleRoutingTask(url, {
213
+ state,
214
+ replace: true,
215
+ reason: "routing initialization",
216
+ });
184
217
  };
185
218
 
186
219
  return {
@@ -1,14 +1,16 @@
1
- import { useFormContext } from "./form_context.js";
1
+ import { useContext } from "preact/hooks";
2
+
3
+ import { FormContext } from "./form_context.js";
2
4
 
3
5
  export const renderActionableComponent = (
4
6
  props,
5
7
  ref,
6
8
  { Basic, WithAction, InsideForm, WithActionInsideForm },
7
9
  ) => {
8
- const { action, shortcuts, ignoreForm } = props;
9
- const formContext = useFormContext();
10
+ const { action, shortcuts } = props;
11
+ const formContext = useContext(FormContext);
10
12
  const hasActionProps = Boolean(action || (shortcuts && shortcuts.length > 0));
11
- const considerInsideForm = ignoreForm ? false : Boolean(formContext);
13
+ const considerInsideForm = Boolean(formContext);
12
14
 
13
15
  if (hasActionProps && WithAction) {
14
16
  if (considerInsideForm && WithActionInsideForm) {