@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
@@ -3,6 +3,27 @@
3
3
 
4
4
  import { useEffect, useLayoutEffect } from "preact/hooks";
5
5
 
6
+ let blurEvent = null;
7
+ let timeout;
8
+ document.body.addEventListener(
9
+ "blur",
10
+ (e) => {
11
+ blurEvent = e;
12
+ setTimeout(() => {
13
+ blurEvent = null;
14
+ });
15
+ },
16
+ { capture: true },
17
+ );
18
+ document.body.addEventListener(
19
+ "focus",
20
+ () => {
21
+ clearTimeout(timeout);
22
+ blurEvent = null;
23
+ },
24
+ { capture: true },
25
+ );
26
+
6
27
  export const useAutoFocus = (
7
28
  focusableElementRef,
8
29
  autoFocus,
@@ -21,19 +42,49 @@ export const useAutoFocus = (
21
42
  focusableElement.scrollLeft = 0;
22
43
  }
23
44
  return () => {
24
- if (
45
+ const focusIsOnSelfOrInsideSelf =
25
46
  document.activeElement === focusableElement ||
26
- document.activeElement === document.body
47
+ focusableElement.contains(document.activeElement);
48
+ if (
49
+ !focusIsOnSelfOrInsideSelf &&
50
+ document.activeElement !== document.body
27
51
  ) {
28
- // if the input is focused when the component is unmounted,
29
- // we restore focus to the element that was focused before
30
- // the input was focused
31
- if (document.body.contains(activeElement)) {
32
- activeElement.focus();
52
+ // focus is not on our element (or body) anymore
53
+ // keep it where it is
54
+ return;
55
+ }
56
+
57
+ // We have focus but we are unmounted
58
+ // -> try to move focus back to something more meaningful that what browser would do
59
+ // (browser would put it to document.body)
60
+ // -> We'll try to move focus back to the element that had focus before we moved it to this element
61
+
62
+ if (!document.body.contains(activeElement)) {
63
+ // previously active element is no longer in the document
64
+ return;
65
+ }
66
+
67
+ if (blurEvent) {
68
+ // But if this element is unmounted during a blur, the element that is about to receive focus should prevail
69
+ const elementAboutToReceiveFocus = blurEvent.relatedTarget;
70
+ const isSelfOrInsideSelf =
71
+ elementAboutToReceiveFocus === focusableElement ||
72
+ focusableElement.contains(elementAboutToReceiveFocus);
73
+ const isPreviouslyActiveElementOrInsideIt =
74
+ elementAboutToReceiveFocus === activeElement ||
75
+ (activeElement && activeElement.contains(elementAboutToReceiveFocus));
76
+ if (!isSelfOrInsideSelf && !isPreviouslyActiveElementOrInsideIt) {
77
+ // the element about to receive focus is not the input itself or inside it
78
+ // and is not the previously active element or inside it
79
+ // -> the element about to receive focus should prevail
80
+ return;
33
81
  }
34
82
  }
83
+
84
+ activeElement.focus();
35
85
  };
36
86
  }, []);
87
+
37
88
  useEffect(() => {
38
89
  if (autoFocus) {
39
90
  const focusableElement = focusableElementRef.current;
@@ -0,0 +1,33 @@
1
+ import { useLayoutEffect, useMemo, useRef } from "preact/hooks";
2
+
3
+ import { useStableCallback } from "./use_stable_callback.js";
4
+
5
+ export const useBatchDuringRender = (callback) => {
6
+ const pendingCallArrayRef = useRef([]);
7
+ const pendingCallArray = pendingCallArrayRef.current;
8
+
9
+ callback = useStableCallback(callback);
10
+ const callbackWithBatching = useMemo(() => {
11
+ return (...args) => {
12
+ if (isRenderingRef.current) {
13
+ pendingCallArray.push(args);
14
+ return;
15
+ }
16
+ callback(...args);
17
+ };
18
+ }, []);
19
+
20
+ const isRenderingRef = useRef();
21
+ isRenderingRef.current = true;
22
+ useLayoutEffect(() => {
23
+ isRenderingRef.current = false;
24
+ if (pendingCallArray.length === 0) {
25
+ return;
26
+ }
27
+ const copy = [...pendingCallArray];
28
+ pendingCallArray.length = 0;
29
+ callback(copy);
30
+ });
31
+
32
+ return callbackWithBatching;
33
+ };
@@ -1,14 +1,14 @@
1
- import { useEffect, useRef, useState } from "preact/hooks";
1
+ import { useLayoutEffect, useRef, useState } from "preact/hooks";
2
2
 
3
3
  export const useDebounceTrue = (value, delay = 300) => {
4
- const [debouncedValue, setDebouncedValue] = useState(false);
4
+ const [debouncedTrue, setDebouncedTrue] = useState(false);
5
5
  const timerRef = useRef(null);
6
6
 
7
- useEffect(() => {
8
- // If value becomes true, start a timer
7
+ useLayoutEffect(() => {
8
+ // If value is true or becomes true, start a timer
9
9
  if (value) {
10
10
  timerRef.current = setTimeout(() => {
11
- setDebouncedValue(true);
11
+ setDebouncedTrue(true);
12
12
  }, delay);
13
13
  } else {
14
14
  // If value becomes false, clear any pending timer and immediately set to false
@@ -16,7 +16,7 @@ export const useDebounceTrue = (value, delay = 300) => {
16
16
  clearTimeout(timerRef.current);
17
17
  timerRef.current = null;
18
18
  }
19
- setDebouncedValue(false);
19
+ setDebouncedTrue(false);
20
20
  }
21
21
 
22
22
  // Cleanup function
@@ -27,5 +27,5 @@ export const useDebounceTrue = (value, delay = 300) => {
27
27
  };
28
28
  }, [value, delay]);
29
29
 
30
- return debouncedValue;
30
+ return debouncedTrue;
31
31
  };
@@ -0,0 +1,35 @@
1
+ /*
2
+ * - Usage
3
+ * useEffect(() => {
4
+ * // here you want to know what has changed, causing useEffect to be called
5
+ * }, [name, value])
6
+ *
7
+ * const diff = useDependenciesDiff({ name, value })
8
+ * useEffect(() => {
9
+ * console.log('useEffect called because', diff)
10
+ * }, [name, value])
11
+ */
12
+
13
+ import { useMemo, useRef } from "preact/hooks";
14
+
15
+ export const useDependenciesDiff = (inputs) => {
16
+ const oldInputsRef = useRef(inputs);
17
+ const inputValuesArray = Object.values(inputs);
18
+ const inputKeysArray = Object.keys(inputs);
19
+ const diffRef = useRef();
20
+ useMemo(() => {
21
+ const oldInputs = oldInputsRef.current;
22
+ const diff = {};
23
+ for (const key of inputKeysArray) {
24
+ const previous = oldInputs[key];
25
+ const current = inputs[key];
26
+ if (previous !== current) {
27
+ diff[key] = { previous, current };
28
+ }
29
+ }
30
+ diffRef.current = diff;
31
+ oldInputsRef.current = inputs;
32
+ }, inputValuesArray);
33
+
34
+ return diffRef.current;
35
+ };
@@ -1,9 +1,10 @@
1
1
  import { initFocusGroup } from "@jsenv/dom";
2
2
  import { useLayoutEffect } from "preact/hooks";
3
3
 
4
- export const useFocusGroup = (elementRef, options) => {
5
- const { direction, skipTab, loop, name, enabled } = options;
6
-
4
+ export const useFocusGroup = (
5
+ elementRef,
6
+ { enabled = true, direction, skipTab, loop, name } = {},
7
+ ) => {
7
8
  useLayoutEffect(() => {
8
9
  if (!enabled) {
9
10
  return null;
@@ -64,41 +64,15 @@ export const useExternalValueSync = (
64
64
  }
65
65
  };
66
66
 
67
- /**
68
- * Hook that handles initial value setup and external value synchronization.
69
- *
70
- * @param {string} name - Parameter name for debugging
71
- * @param {any} externalValue - Value from props or parent component
72
- * @param {any} fallbackValue - Backup value if external value isn't useful
73
- * @param {any} defaultValue - Final fallback value
74
- * @param {Function} setValue - Function to call when value needs to be set
75
- *
76
- * @returns {any} The resolved initial value
77
- */
78
- export const useInitialValue = (
79
- name,
80
- externalValue,
81
- fallbackValue,
82
- defaultValue,
83
- setValue,
84
- ) => {
85
- const initialValue = resolveInitialValue(
86
- externalValue,
87
- fallbackValue,
88
- defaultValue,
89
- );
90
-
91
- // Set initial value on mount
92
- const mountedRef = useRef(false);
93
- if (!mountedRef.current) {
94
- mountedRef.current = true;
95
- if (name) {
96
- setValue(initialValue);
97
- }
67
+ const UNSET = {};
68
+ export const useInitialValue = (compute) => {
69
+ const initialValueRef = useRef(UNSET);
70
+ let initialValue = initialValueRef.current;
71
+ if (initialValue !== UNSET) {
72
+ return initialValue;
98
73
  }
99
74
 
100
- // Use the new sync hook
101
- useExternalValueSync(externalValue, defaultValue, setValue, name);
102
-
75
+ initialValue = compute();
76
+ initialValueRef.current = initialValue;
103
77
  return initialValue;
104
78
  };
@@ -20,7 +20,7 @@ import { useRef } from "preact/hooks";
20
20
  * const nameSignal = useSignalSync(file.name);
21
21
  *
22
22
  * return (
23
- * <EditableText
23
+ * <Editable
24
24
  * valueSignal={nameSignal} // User can edit this
25
25
  * action={renameFileAction} // Saves changes
26
26
  * />
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Custom hook creating a stable callback that doesn't trigger re-renders.
3
+ *
4
+ * PROBLEM: Parent components often forget to use useCallback, causing library
5
+ * components to re-render unnecessarily when receiving callback props.
6
+ *
7
+ * SOLUTION: Library components can use this hook to create stable callback
8
+ * references internally, making them defensive against parents who don't
9
+ * optimize their callbacks. This ensures library components don't force
10
+ * consumers to think about useCallback.
11
+ *
12
+ * USAGE:
13
+ * ```js
14
+ * // Parent component (consumer) - no useCallback needed
15
+ * const Parent = () => {
16
+ * const [count, setCount] = useState(0);
17
+ *
18
+ * // Parent naturally creates new function reference each render
19
+ * // (forgetting useCallback is common and shouldn't break performance)
20
+ * return <LibraryButton onClick={(e) => setCount(count + 1)} />;
21
+ * };
22
+ *
23
+ * // Library component - defensive against changing callbacks
24
+ * const LibraryButton = ({ onClick }) => {
25
+ * // ✅ Create stable reference from parent's potentially changing callback
26
+ * const stableClick = useStableCallback(onClick);
27
+ *
28
+ * // Internal expensive components won't re-render when parent updates
29
+ * return <ExpensiveInternalButton onClick={stableClick} />;
30
+ * };
31
+ *
32
+ * // Deep internal component gets stable reference
33
+ * const ExpensiveInternalButton = memo(({ onClick }) => {
34
+ * // This won't re-render when Parent's count changes
35
+ * // But onClick will always call the latest Parent callback
36
+ * return <button onClick={onClick}>Click me</button>;
37
+ * });
38
+ * ```
39
+ *
40
+ * Perfect for library components that need performance without burdening consumers.
41
+ */
42
+
43
+ import { useRef } from "preact/hooks";
44
+
45
+ export const useStableCallback = (callback, mapper) => {
46
+ const callbackRef = useRef();
47
+ callbackRef.current = callback;
48
+ const stableCallbackRef = useRef();
49
+
50
+ // Return original falsy value directly when callback is not a function
51
+ if (!callback) {
52
+ return callback;
53
+ }
54
+
55
+ const existingStableCallback = stableCallbackRef.current;
56
+ if (existingStableCallback) {
57
+ return existingStableCallback;
58
+ }
59
+ const stableCallback = (...args) => {
60
+ const currentCallback = callbackRef.current;
61
+ if (mapper) {
62
+ args = mapper(...args);
63
+ }
64
+ return currentCallback(...args);
65
+ };
66
+ stableCallbackRef.current = stableCallback;
67
+ return stableCallback;
68
+ };
@@ -1,28 +1,35 @@
1
- import { useCallback, useState } from "preact/hooks";
1
+ import { useCallback, useRef, useState } from "preact/hooks";
2
2
  import { addIntoArray, removeFromArray } from "../utils/array_add_remove.js";
3
3
  import {
4
4
  resolveInitialValue,
5
5
  useExternalValueSync,
6
6
  } from "./use_initial_value.js";
7
7
 
8
+ const FIRST_MOUNT = {};
8
9
  export const useStateArray = (
9
- externalValue = [],
10
+ externalValue,
10
11
  fallbackValue,
11
12
  defaultValue = [],
12
13
  ) => {
13
- const initialValue = resolveInitialValue(
14
- externalValue,
15
- fallbackValue,
16
- defaultValue,
17
- );
14
+ const initialValueRef = useRef(FIRST_MOUNT);
15
+ if (initialValueRef.current === FIRST_MOUNT) {
16
+ const initialValue = resolveInitialValue(
17
+ externalValue,
18
+ fallbackValue,
19
+ defaultValue,
20
+ );
21
+ initialValueRef.current = initialValue;
22
+ }
23
+ const initialValue = initialValueRef.current;
18
24
  const [array, setArray] = useState(initialValue);
19
25
 
20
- // Sync external value changes
26
+ // Only sync external value changes if externalValue was explicitly provided
21
27
  useExternalValueSync(externalValue, defaultValue, setArray, "state_array");
22
28
 
23
29
  const add = useCallback((valueToAdd) => {
24
30
  setArray((array) => {
25
- return addIntoArray(array, valueToAdd);
31
+ const newArray = addIntoArray(array, valueToAdd);
32
+ return newArray;
26
33
  });
27
34
  }, []);
28
35
 
@@ -13,6 +13,7 @@
13
13
  - ```js
14
14
 
15
15
  ```
16
+
16
17
  - const getUserTemplate = createActionTemplate(async ({ userId }) => {
17
18
  - const response = await fetch(`/api/users/${userId}`);
18
19
  - return response.json();
@@ -20,6 +21,7 @@
20
21
  - ```
21
22
 
22
23
  ```
24
+
23
25
  -
24
26
  - ### 🎯 **Action Instances**
25
27
  - Stateful objects created from templates with specific parameters. Each unique parameter set
@@ -27,17 +29,20 @@
27
29
  - ```js
28
30
 
29
31
  ```
32
+
30
33
  - const userAction = getUserTemplate.instantiate({ userId: 123 });
31
34
  - const status = useActionStatus(userAction); // { pending, data, error, ... }
32
35
  - ```
33
36
 
34
37
  ```
38
+
35
39
  -
36
40
  - ### 🔄 **Action Proxies**
37
41
  - Dynamic actions that react to signal changes, automatically reloading when parameters change.
38
42
  - ```js
39
43
 
40
44
  ```
45
+
41
46
  - const userProxy = createActionProxy(getUserTemplate, {
42
47
  - userId: userIdSignal, // Signal - reactive
43
48
  - includeProfile: true // Static - not reactive
@@ -46,6 +51,7 @@
46
51
  - ```
47
52
 
48
53
  ```
54
+
49
55
  -
50
56
  - ## Loading States & Lifecycle
51
57
  -
@@ -77,6 +83,7 @@
77
83
  - ```js
78
84
 
79
85
  ```
86
+
80
87
  - const baseAction = getUserTemplate.instantiate({ userId: 123 });
81
88
  - const enrichedAction = baseAction.bindParams({ includeProfile: true });
82
89
  - // Result: { userId: 123, includeProfile: true }
@@ -86,6 +93,7 @@
86
93
  - ```
87
94
 
88
95
  ```
96
+
89
97
  -
90
98
  - ### 🎮 **Concurrent Loading Control**
91
99
  - - Prevents duplicate requests for same resource
@@ -96,6 +104,7 @@
96
104
  - ```js
97
105
 
98
106
  ```
107
+
99
108
  - const actionTemplate = createActionTemplate(callback, {
100
109
  - sideEffect: (params, loadParams) => {
101
110
  - // Setup logic (analytics, subscriptions, etc.)
@@ -107,6 +116,7 @@
107
116
  - ```
108
117
 
109
118
  ```
119
+
110
120
  -
111
121
  - ## Usage Patterns
112
122
  -
@@ -114,6 +124,7 @@
114
124
  - ```js
115
125
 
116
126
  ```
127
+
117
128
  - const getUserAction = createActionTemplate(async ({ userId }) => {
118
129
  - return await api.getUser(userId);
119
130
  - });
@@ -128,11 +139,13 @@
128
139
  - ```
129
140
 
130
141
  ```
142
+
131
143
  -
132
144
  - ### 🔄 **Reactive Data Loading**
133
145
  - ```js
134
146
 
135
147
  ```
148
+
136
149
  - const searchProxy = createActionProxy(searchTemplate, {
137
150
  - query: searchSignal,
138
151
  - filters: filtersSignal
@@ -141,11 +154,13 @@
141
154
  - ```
142
155
 
143
156
  ```
157
+
144
158
  -
145
159
  - ### 📋 **Master-Detail Pattern**
146
160
  - ```js
147
161
 
148
162
  ```
163
+
149
164
  - const usersAction = getUsersTemplate.instantiate();
150
165
  - const selectedUser = signal(null);
151
166
  -
@@ -155,11 +170,13 @@
155
170
  - ```
156
171
 
157
172
  ```
173
+
158
174
  -
159
175
  - ### 🏃 **Progressive Loading**
160
176
  - ```js
161
177
 
162
178
  ```
179
+
163
180
  - // Preload on hover, load on click
164
181
  - <button
165
182
  - onMouseEnter={() => action.preload()}
@@ -170,6 +187,7 @@
170
187
  - ```
171
188
 
172
189
  ```
190
+
173
191
  -
174
192
  - ## Advanced Features
175
193
  -
@@ -177,6 +195,7 @@
177
195
  - ```js
178
196
 
179
197
  ```
198
+
180
199
  - const actionTemplate = createActionTemplate(fetchUser, {
181
200
  - computedDataSignal: computed(() => {
182
201
  - const rawData = dataSignal.value;
@@ -186,11 +205,13 @@
186
205
  - ```
187
206
 
188
207
  ```
208
+
189
209
  -
190
210
  - ### 🎨 **Async Rendering Support**
191
211
  - ```js
192
212
 
193
213
  ```
214
+
194
215
  - const actionTemplate = createActionTemplate(fetchData, {
195
216
  - renderLoadedAsync: async () => {
196
217
  - const { UserComponent } = await import('./UserComponent.js');
@@ -200,6 +221,7 @@
200
221
  - ```
201
222
 
202
223
  ```
224
+
203
225
  -
204
226
  - ### 🛠️ **Debugging & Observability**
205
227
  - Built-in debug mode with detailed logging of state transitions, loading coordination,
package/src/notes.md CHANGED
@@ -1,13 +1,34 @@
1
- - import.meta.css during build should use stylesheet to inject so that it puts an url instead of constructed stylesheet?
1
+ - Drag to move row and column
2
+
3
+ (ideally we keep an empty "clone" in the table and we create a visually identic clone to drag)
4
+
5
+ sitcky prevents drag to re-order
6
+ I think we are starting to reach the limits of a table element
7
+
8
+ donc on va refaire mais avec des div comme ca on controle bien tout
9
+ de toute facon les dimensions des lignes et colonnes seront fixes
10
+ et je gagne pas grand chose a passer par la balise table
11
+
12
+ - Ideally the drag gesture should autoscroll once dragged element boundaries reach the scrollable parent (not the mouse)
13
+
14
+ - Can use shortcuts on table selection
15
+ - cmd + delete would delete rows/columns
16
+ on cells it does nothing but we'll be able to copy via keyboard to start
17
+
18
+ - Fixed first column and first row (overflow on the rest + it's fixed when there is a lof of content)
19
+
20
+ - Can delete a table row
2
21
 
3
- - La table qui affiche une page, on mettra les details dans un bouton settings en haut a droite
4
- Qui ouvrira les infos a propos de cette tables et on focus sur les données au lieu des settings de la table
5
- (surement qu'on aura
6
- - api/tables/:tablename/settings
7
- - api/tables/:tablename/columns
8
- - api/tables/:tablename/rows
9
- (et api/tables on verra je serais pas encore)
10
-
11
- - See table columns, ability to add, update, remove table columns
12
- - Ability to see table rows, ability to add/update/remove rows
13
- - Pagination of course
22
+ - Can update a table row cell
23
+
24
+ - Can see table columns attributes
25
+
26
+ - Can update table column attributes
27
+
28
+ - Can remove table column
29
+
30
+ - Can move table column
31
+
32
+ - Pagination
33
+
34
+ - import.meta.css during build should use stylesheet to inject so that it puts an url instead of constructed stylesheet?