@jsenv/navi 0.10.1 → 0.11.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 (207) hide show
  1. package/dist/jsenv_navi.js +13858 -23295
  2. package/dist/jsenv_navi.js.map +1281 -0
  3. package/package.json +5 -7
  4. package/index.js +0 -122
  5. package/src/action_private_properties.js +0 -11
  6. package/src/action_proxy_test.html +0 -353
  7. package/src/action_run_states.js +0 -5
  8. package/src/actions.js +0 -1401
  9. package/src/browser_integration/browser_integration.js +0 -216
  10. package/src/browser_integration/document_back_and_forward.js +0 -17
  11. package/src/browser_integration/document_loading_signal.js +0 -100
  12. package/src/browser_integration/document_state_signal.js +0 -9
  13. package/src/browser_integration/document_url_signal.js +0 -9
  14. package/src/browser_integration/use_is_visited.js +0 -19
  15. package/src/browser_integration/via_history.js +0 -232
  16. package/src/browser_integration/via_navigation.js +0 -168
  17. package/src/components/action_execution/form_context.js +0 -5
  18. package/src/components/action_execution/render_actionable_component.jsx +0 -29
  19. package/src/components/action_execution/use_action.js +0 -99
  20. package/src/components/action_execution/use_execute_action.js +0 -177
  21. package/src/components/action_execution/use_run_on_mount.js +0 -9
  22. package/src/components/action_renderer.jsx +0 -125
  23. package/src/components/callout/callout.js +0 -990
  24. package/src/components/callout/callout_demo.html +0 -201
  25. package/src/components/callout/test_dynamic_positioning.html +0 -161
  26. package/src/components/callout/test_html_document_iframe.html +0 -182
  27. package/src/components/demos/0_button_demo.html +0 -707
  28. package/src/components/demos/10_column_reordering_debug.html +0 -277
  29. package/src/components/demos/11_table_selection_debug.html +0 -432
  30. package/src/components/demos/1_checkbox_demo.html +0 -754
  31. package/src/components/demos/2_input_textual_demo.html +0 -286
  32. package/src/components/demos/3_radio_demo.html +0 -874
  33. package/src/components/demos/4_select_demo.html +0 -100
  34. package/src/components/demos/5_list_scrollable_demo.html +0 -153
  35. package/src/components/demos/6_tablist_demo.html +0 -77
  36. package/src/components/demos/7_table_selection_demo.html +0 -176
  37. package/src/components/demos/8_table_fixed_headers_demo.html +0 -584
  38. package/src/components/demos/9_table_column_drag_demo.html +0 -325
  39. package/src/components/demos/action/0_button_demo.html +0 -204
  40. package/src/components/demos/action/10_shortcuts_demo.html +0 -189
  41. package/src/components/demos/action/11_nested_shortcuts_demo.xhtml +0 -401
  42. package/src/components/demos/action/1_input_text_demo.html +0 -876
  43. package/src/components/demos/action/2_form_multiple.html +0 -303
  44. package/src/components/demos/action/3_details_demo.html +0 -203
  45. package/src/components/demos/action/4_input_checkbox_demo.html +0 -731
  46. package/src/components/demos/action/5_input_checkbox_state_demo.html +0 -270
  47. package/src/components/demos/action/6_checkbox_list_demo.html +0 -341
  48. package/src/components/demos/action/7_radio_list_demo.html +0 -357
  49. package/src/components/demos/action/8_editable_demo.html +0 -431
  50. package/src/components/demos/action/9_link_demo.html +0 -194
  51. package/src/components/demos/demo.md +0 -0
  52. package/src/components/demos/route/basic/basic.html +0 -14
  53. package/src/components/demos/route/basic/basic_route_demo.jsx +0 -224
  54. package/src/components/demos/route/multi/multi.html +0 -14
  55. package/src/components/demos/route/multi/multi_route_demo.jsx +0 -277
  56. package/src/components/demos/ui_transition/0_action_renderer_ui_transition_demo.html +0 -695
  57. package/src/components/demos/ui_transition/1_nested_ui_transition_demo.html +0 -429
  58. package/src/components/demos/ui_transition/2_height_transition_test.html +0 -295
  59. package/src/components/details/details.jsx +0 -245
  60. package/src/components/details/summary_marker.jsx +0 -141
  61. package/src/components/edition/editable.jsx +0 -186
  62. package/src/components/error_boundary_context.js +0 -9
  63. package/src/components/field/README.md +0 -247
  64. package/src/components/field/button.jsx +0 -429
  65. package/src/components/field/checkbox_list.jsx +0 -185
  66. package/src/components/field/collect_form_element_values.js +0 -82
  67. package/src/components/field/custom_field.js +0 -106
  68. package/src/components/field/form.jsx +0 -209
  69. package/src/components/field/input.jsx +0 -16
  70. package/src/components/field/input_checkbox.jsx +0 -434
  71. package/src/components/field/input_radio.jsx +0 -432
  72. package/src/components/field/input_textual.jsx +0 -389
  73. package/src/components/field/label.jsx +0 -46
  74. package/src/components/field/radio_list.jsx +0 -183
  75. package/src/components/field/select.jsx +0 -256
  76. package/src/components/field/use_action_events.js +0 -132
  77. package/src/components/field/use_form_events.js +0 -59
  78. package/src/components/field/use_ui_state_controller.js +0 -506
  79. package/src/components/item_tracker/README.md +0 -461
  80. package/src/components/item_tracker/use_isolated_item_tracker.jsx +0 -209
  81. package/src/components/item_tracker/use_isolated_item_tracker_demo.html +0 -148
  82. package/src/components/item_tracker/use_isolated_item_tracker_demo.jsx +0 -460
  83. package/src/components/item_tracker/use_item_tracker.jsx +0 -143
  84. package/src/components/item_tracker/use_item_tracker_demo.html +0 -207
  85. package/src/components/item_tracker/use_item_tracker_demo.jsx +0 -216
  86. package/src/components/keyboard_shortcuts/active_keyboard_shortcuts.jsx +0 -87
  87. package/src/components/keyboard_shortcuts/aria_key_shortcuts.js +0 -61
  88. package/src/components/keyboard_shortcuts/keyboard_key_meta.js +0 -17
  89. package/src/components/keyboard_shortcuts/keyboard_shortcuts.js +0 -371
  90. package/src/components/keyboard_shortcuts/os.js +0 -9
  91. package/src/components/layout/demos/demo_flex.html +0 -638
  92. package/src/components/layout/demos/demo_layout_style_buttons.html +0 -351
  93. package/src/components/layout/demos/demo_layout_style_input.html +0 -226
  94. package/src/components/layout/demos/demo_layout_style_text.html +0 -514
  95. package/src/components/layout/flex.jsx +0 -109
  96. package/src/components/layout/layout_context.jsx +0 -3
  97. package/src/components/layout/spacing.jsx +0 -20
  98. package/src/components/layout/use_layout_style.js +0 -249
  99. package/src/components/link/link.jsx +0 -267
  100. package/src/components/link/link_with_icon.jsx +0 -52
  101. package/src/components/loader/loader_background.jsx +0 -372
  102. package/src/components/loader/loading_spinner.jsx +0 -68
  103. package/src/components/loader/network_speed.js +0 -83
  104. package/src/components/loader/rectangle_loading.jsx +0 -244
  105. package/src/components/props_composition/demos/demo_with_props_style.html +0 -81
  106. package/src/components/props_composition/with_props_class_name.js +0 -37
  107. package/src/components/props_composition/with_props_style.js +0 -26
  108. package/src/components/route.jsx +0 -19
  109. package/src/components/selection/selection.jsx +0 -1583
  110. package/src/components/svg/font_sized_svg.jsx +0 -59
  111. package/src/components/svg/icon_and_text.jsx +0 -21
  112. package/src/components/svg/svg_mask_overlay.jsx +0 -105
  113. package/src/components/table/drag/table_drag.jsx +0 -506
  114. package/src/components/table/resize/table_resize.jsx +0 -650
  115. package/src/components/table/resize/table_size.js +0 -43
  116. package/src/components/table/selection/table_selection.js +0 -106
  117. package/src/components/table/selection/table_selection.jsx +0 -203
  118. package/src/components/table/sticky/sticky_group.js +0 -354
  119. package/src/components/table/sticky/table_sticky.js +0 -25
  120. package/src/components/table/sticky/table_sticky.jsx +0 -501
  121. package/src/components/table/table.jsx +0 -721
  122. package/src/components/table/table_css.js +0 -211
  123. package/src/components/table/table_ui.jsx +0 -49
  124. package/src/components/table/use_cells_and_columns.js +0 -90
  125. package/src/components/table/use_object_array_to_cells.js +0 -46
  126. package/src/components/table/z_indexes.js +0 -23
  127. package/src/components/tablist/tablist.jsx +0 -99
  128. package/src/components/text/demos/demo_text_and_icon.html +0 -421
  129. package/src/components/text/overflow.jsx +0 -15
  130. package/src/components/text/text.jsx +0 -83
  131. package/src/components/text/text_and_count.jsx +0 -28
  132. package/src/components/ui_transition.jsx +0 -128
  133. package/src/components/use_auto_focus.js +0 -94
  134. package/src/components/use_batch_during_render.js +0 -33
  135. package/src/components/use_debounce_true.js +0 -31
  136. package/src/components/use_dependencies_diff.js +0 -35
  137. package/src/components/use_focus_group.js +0 -20
  138. package/src/components/use_initial_value.js +0 -78
  139. package/src/components/use_is_visited.js +0 -19
  140. package/src/components/use_ref_array.js +0 -38
  141. package/src/components/use_signal_sync.js +0 -50
  142. package/src/components/use_stable_callback.js +0 -68
  143. package/src/components/use_state_array.js +0 -47
  144. package/src/docs/actions.md +0 -250
  145. package/src/docs/demos/resource/action_status.jsx +0 -42
  146. package/src/docs/demos/resource/demo.md +0 -1
  147. package/src/docs/demos/resource/resource_demo_0.html +0 -84
  148. package/src/docs/demos/resource/resource_demo_10_post_gc.html +0 -364
  149. package/src/docs/demos/resource/resource_demo_11_describe_many.html +0 -362
  150. package/src/docs/demos/resource/resource_demo_2.html +0 -173
  151. package/src/docs/demos/resource/resource_demo_3_filtered_users.html +0 -415
  152. package/src/docs/demos/resource/resource_demo_4_details.html +0 -284
  153. package/src/docs/demos/resource/resource_demo_5_renderer_lazy.html +0 -115
  154. package/src/docs/demos/resource/resource_demo_6_gc.html +0 -217
  155. package/src/docs/demos/resource/resource_demo_7_child_gc.html +0 -240
  156. package/src/docs/demos/resource/resource_demo_8_proxy_gc.html +0 -319
  157. package/src/docs/demos/resource/resource_demo_9_describe_one.html +0 -472
  158. package/src/docs/demos/resource/tata.jsx +0 -3
  159. package/src/docs/demos/resource/toto.jsx +0 -3
  160. package/src/docs/demos/user_nav/user_nav.html +0 -12
  161. package/src/docs/demos/user_nav/user_nav.jsx +0 -330
  162. package/src/docs/resource_dependencies.md +0 -103
  163. package/src/docs/resource_with_params.md +0 -80
  164. package/src/navi_css_vars.js +0 -14
  165. package/src/notes.md +0 -34
  166. package/src/route/route.js +0 -596
  167. package/src/route/route.xtest.html +0 -228
  168. package/src/store/array_signal_store.js +0 -537
  169. package/src/store/local_storage_signal.js +0 -17
  170. package/src/store/resource_graph.js +0 -1304
  171. package/src/store/tests/resource_graph_autoreload_demo.html +0 -12
  172. package/src/store/tests/resource_graph_autoreload_demo.jsx +0 -964
  173. package/src/store/tests/resource_graph_dependencies.test_manual.js +0 -95
  174. package/src/store/value_in_local_storage.js +0 -187
  175. package/src/symbol_object_signal.js +0 -1
  176. package/src/use_action_data.js +0 -10
  177. package/src/use_action_status.js +0 -47
  178. package/src/utils/add_many_event_listeners.js +0 -15
  179. package/src/utils/array_add_remove.js +0 -61
  180. package/src/utils/array_signal.js +0 -15
  181. package/src/utils/compare_two_js_values.js +0 -172
  182. package/src/utils/execute_with_cleanup.js +0 -21
  183. package/src/utils/get_caller_info.js +0 -85
  184. package/src/utils/is_signal.js +0 -20
  185. package/src/utils/js_value_weak_map.js +0 -162
  186. package/src/utils/js_value_weak_map_demo.html +0 -690
  187. package/src/utils/merge_two_js_values.js +0 -53
  188. package/src/utils/stringify_for_display.js +0 -131
  189. package/src/utils/weak_effect.js +0 -48
  190. package/src/validation/constraints/confirm_constraint.js +0 -14
  191. package/src/validation/constraints/create_unique_value_constraint.js +0 -27
  192. package/src/validation/constraints/native_constraints.js +0 -338
  193. package/src/validation/constraints/readonly_constraint.js +0 -41
  194. package/src/validation/constraints/same_as_constraint.js +0 -42
  195. package/src/validation/constraints/single_space_constraint.js +0 -13
  196. package/src/validation/custom_constraint_validation.js +0 -793
  197. package/src/validation/custom_message.js +0 -18
  198. package/src/validation/demos/browser_style.png +0 -0
  199. package/src/validation/demos/demo_same_as_constraint.html +0 -259
  200. package/src/validation/demos/form_validation_demo.html +0 -142
  201. package/src/validation/demos/form_validation_demo_preact.html +0 -87
  202. package/src/validation/demos/form_validation_native_popover_demo.html +0 -168
  203. package/src/validation/demos/form_validation_vs_native_demo.html +0 -172
  204. package/src/validation/hooks/use_constraints.js +0 -23
  205. package/src/validation/hooks/use_custom_validation_ref.js +0 -73
  206. package/src/validation/hooks/use_validation_message.js +0 -19
  207. package/src/validation/input_change_effect.js +0 -106
@@ -1,106 +0,0 @@
1
- import { createContext } from "preact";
2
- import { useMemo } from "preact/hooks";
3
-
4
- export const TableSelectionContext = createContext();
5
- export const useTableSelectionContextValue = (
6
- selection,
7
- selectionController,
8
- ) => {
9
- const selectionContextValue = useMemo(() => {
10
- const selectedColumnIds = [];
11
- const selectedRowIds = [];
12
- const selectedCellIds = [];
13
- const columnIdWithSomeSelectedCellSet = new Set();
14
- const rowIdWithSomeSelectedCellSet = new Set();
15
- for (const item of selection) {
16
- const selectionValueInfo = parseTableSelectionValue(item);
17
- if (selectionValueInfo.type === "row") {
18
- const { rowId } = selectionValueInfo;
19
- selectedRowIds.push(rowId);
20
- continue;
21
- }
22
- if (selectionValueInfo.type === "column") {
23
- const { columnId } = selectionValueInfo;
24
- selectedColumnIds.push(columnId);
25
- continue;
26
- }
27
- if (selectionValueInfo.type === "cell") {
28
- const { cellId, columnId, rowId } = selectionValueInfo;
29
- selectedCellIds.push(cellId);
30
- columnIdWithSomeSelectedCellSet.add(columnId);
31
- rowIdWithSomeSelectedCellSet.add(rowId);
32
- continue;
33
- }
34
- }
35
- return {
36
- selection,
37
- selectionController,
38
- selectedColumnIds,
39
- selectedRowIds,
40
- columnIdWithSomeSelectedCellSet,
41
- rowIdWithSomeSelectedCellSet,
42
- };
43
- }, [selection]);
44
-
45
- return selectionContextValue;
46
- };
47
-
48
- export const parseTableSelectionValue = (selectionValue) => {
49
- if (selectionValue.startsWith("column:")) {
50
- const columnId = selectionValue.slice("column:".length);
51
- return { type: "column", columnId };
52
- }
53
- if (selectionValue.startsWith("row:")) {
54
- const rowId = selectionValue.slice("row:".length);
55
- return { type: "row", rowId };
56
- }
57
- const cellId = selectionValue.slice("cell:".length);
58
- const [columnId, rowId] = cellId.split("-");
59
- return { type: "cell", cellId, columnId, rowId };
60
- };
61
- export const stringifyTableSelectionValue = (type, value) => {
62
- if (type === "cell") {
63
- const { columnId, rowId } = value;
64
- return `cell:${columnId}-${rowId}`;
65
- }
66
- if (type === "column") {
67
- return `column:${value}`;
68
- }
69
- if (type === "row") {
70
- return `row:${value}`;
71
- }
72
- return "";
73
- };
74
-
75
- /**
76
- * Check if a specific cell is selected
77
- * @param {Array} selection - The selection set or array
78
- * @param {{rowIndex: number, columnIndex: number}} cellPosition - Cell coordinates
79
- * @returns {boolean} True if the cell is selected
80
- */
81
- export const isCellSelected = (selection, cellId) => {
82
- const cellSelectionValue = stringifyTableSelectionValue("cell", cellId);
83
- return selection.includes(cellSelectionValue);
84
- };
85
-
86
- /**
87
- * Check if a specific row is selected
88
- * @param {Array} selection - The selection set or array
89
- * @param {number} rowIndex - Row index
90
- * @returns {boolean} True if the row is selected
91
- */
92
- export const isRowSelected = (selection, rowId) => {
93
- const rowSelectionValue = stringifyTableSelectionValue("row", rowId);
94
- return selection.includes(rowSelectionValue);
95
- };
96
-
97
- /**
98
- * Check if a specific column is selected
99
- * @param {Array} selection - The selection set or array
100
- * @param {number} columnIndex - Column index
101
- * @returns {boolean} True if the column is selected
102
- */
103
- export const isColumnSelected = (selection, columnId) => {
104
- const columnSelectionValue = stringifyTableSelectionValue("column", columnId);
105
- return selection.has(columnSelectionValue);
106
- };
@@ -1,203 +0,0 @@
1
- import { useLayoutEffect } from "preact/hooks";
2
-
3
- import { useSelectionController } from "../../selection/selection.jsx";
4
-
5
- import.meta.css = /* css */ `
6
- body {
7
- --selection-border-color: #0078d4;
8
- --selection-background-color: #eaf1fd;
9
- }
10
-
11
- .navi_table_cell[aria-selected="true"] {
12
- background-color: var(--selection-background-color);
13
- }
14
-
15
- /* One border */
16
- .navi_table_cell[data-selection-border-top]::after {
17
- box-shadow: inset 0 1px 0 0 var(--selection-border-color);
18
- }
19
- .navi_table_cell[data-selection-border-right]::after {
20
- box-shadow: inset -1px 0 0 0 var(--selection-border-color);
21
- }
22
- .navi_table_cell[data-selection-border-bottom]::after {
23
- box-shadow: inset 0 -1px 0 0 var(--selection-border-color);
24
- }
25
- .navi_table_cell[data-selection-border-left]::after {
26
- box-shadow: inset 1px 0 0 0 var(--selection-border-color);
27
- }
28
-
29
- /* Two border combinations */
30
- .navi_table_cell[data-selection-border-top][data-selection-border-right]::after {
31
- box-shadow:
32
- inset 0 1px 0 0 var(--selection-border-color),
33
- inset -1px 0 0 0 var(--selection-border-color);
34
- }
35
- .navi_table_cell[data-selection-border-top][data-selection-border-bottom]::after {
36
- box-shadow:
37
- inset 0 1px 0 0 var(--selection-border-color),
38
- inset 0 -1px 0 0 var(--selection-border-color);
39
- }
40
- .navi_table_cell[data-selection-border-top][data-selection-border-left]::after {
41
- box-shadow:
42
- inset 0 1px 0 0 var(--selection-border-color),
43
- inset 1px 0 0 0 var(--selection-border-color);
44
- }
45
- .navi_table_cell[data-selection-border-right][data-selection-border-bottom]::after {
46
- box-shadow:
47
- inset -1px 0 0 0 var(--selection-border-color),
48
- inset 0 -1px 0 0 var(--selection-border-color);
49
- }
50
- .navi_table_cell[data-selection-border-right][data-selection-border-left]::after {
51
- box-shadow:
52
- inset -1px 0 0 0 var(--selection-border-color),
53
- inset 1px 0 0 0 var(--selection-border-color);
54
- }
55
- .navi_table_cell[data-selection-border-bottom][data-selection-border-left]::after {
56
- box-shadow:
57
- inset 0 -1px 0 0 var(--selection-border-color),
58
- inset 1px 0 0 0 var(--selection-border-color);
59
- }
60
-
61
- /* Three border combinations */
62
- .navi_table_cell[data-selection-border-top][data-selection-border-right][data-selection-border-bottom]::after {
63
- box-shadow:
64
- inset 0 1px 0 0 var(--selection-border-color),
65
- inset -1px 0 0 0 var(--selection-border-color),
66
- inset 0 -1px 0 0 var(--selection-border-color);
67
- }
68
- .navi_table_cell[data-selection-border-top][data-selection-border-bottom][data-selection-border-left]::after {
69
- box-shadow:
70
- inset 0 1px 0 0 var(--selection-border-color),
71
- inset 0 -1px 0 0 var(--selection-border-color),
72
- inset 1px 0 0 0 var(--selection-border-color);
73
- }
74
- .navi_table_cell[data-selection-border-top][data-selection-border-right][data-selection-border-left]::after {
75
- box-shadow:
76
- inset 0 1px 0 0 var(--selection-border-color),
77
- inset -1px 0 0 0 var(--selection-border-color),
78
- inset 1px 0 0 0 var(--selection-border-color);
79
- }
80
- .navi_table_cell[data-selection-border-right][data-selection-border-bottom][data-selection-border-left]::after {
81
- box-shadow:
82
- inset -1px 0 0 0 var(--selection-border-color),
83
- inset 0 -1px 0 0 var(--selection-border-color),
84
- inset 1px 0 0 0 var(--selection-border-color);
85
- }
86
-
87
- /* Four border combinations (full selection) */
88
- .navi_table_cell[data-selection-border-top][data-selection-border-right][data-selection-border-bottom][data-selection-border-left]::after {
89
- box-shadow:
90
- inset 0 1px 0 0 var(--selection-border-color),
91
- inset -1px 0 0 0 var(--selection-border-color),
92
- inset 0 -1px 0 0 var(--selection-border-color),
93
- inset 1px 0 0 0 var(--selection-border-color);
94
- }
95
- `;
96
-
97
- export const useTableSelectionController = ({
98
- tableRef,
99
- selection,
100
- onSelectionChange,
101
- selectionColor,
102
- }) => {
103
- const selectionController = useSelectionController({
104
- elementRef: tableRef,
105
- layout: "grid",
106
- value: selection,
107
- onChange: onSelectionChange,
108
- selectAllName: "cell",
109
- });
110
-
111
- useLayoutEffect(() => {
112
- const table = tableRef.current;
113
- if (!table) {
114
- return;
115
- }
116
- updateSelectionBorders(table, selectionController);
117
- }, [selectionController.value]);
118
-
119
- useLayoutEffect(() => {
120
- const table = tableRef.current;
121
- if (!table) {
122
- return;
123
- }
124
- if (selectionColor) {
125
- table.style.setProperty("--selection-border-color", selectionColor);
126
- } else {
127
- table.style.removeProperty("--selection-border-color");
128
- }
129
- }, [selectionColor]);
130
-
131
- return selectionController;
132
- };
133
-
134
- const updateSelectionBorders = (tableElement, selectionController) => {
135
- // Find all selected cells
136
- const cells = Array.from(tableElement.querySelectorAll(".navi_table_cell"));
137
- const selectedCells = [];
138
- for (const cell of cells) {
139
- if (selectionController.isElementSelected(cell)) {
140
- selectedCells.push(cell);
141
- }
142
- }
143
-
144
- // Clear all existing selection border attributes
145
- tableElement
146
- .querySelectorAll(
147
- "[data-selection-border-top], [data-selection-border-right], [data-selection-border-bottom], [data-selection-border-left]",
148
- )
149
- .forEach((cell) => {
150
- cell.removeAttribute("data-selection-border-top");
151
- cell.removeAttribute("data-selection-border-right");
152
- cell.removeAttribute("data-selection-border-bottom");
153
- cell.removeAttribute("data-selection-border-left");
154
- });
155
-
156
- if (selectedCells.length === 0) {
157
- return;
158
- }
159
-
160
- // Convert NodeList to array and get cell positions
161
-
162
- const cellPositions = selectedCells.map((cell) => {
163
- const row = cell.parentElement;
164
- const allRows = Array.from(tableElement.querySelectorAll(".navi_tr"));
165
- return {
166
- element: cell,
167
- rowIndex: allRows.indexOf(row),
168
- columnIndex: Array.from(row.children).indexOf(cell),
169
- };
170
- });
171
-
172
- // Create a set for fast lookup of selected cell positions
173
- const selectedPositions = new Set(
174
- cellPositions.map((pos) => `${pos.rowIndex},${pos.columnIndex}`),
175
- );
176
-
177
- // Apply selection borders based on actual neighbors (for proper L-shaped selection support)
178
- cellPositions.forEach(({ element, rowIndex, columnIndex }) => {
179
- // Top border: if cell above is NOT selected or doesn't exist
180
- const cellAbove = `${rowIndex - 1},${columnIndex}`;
181
- if (!selectedPositions.has(cellAbove)) {
182
- element.setAttribute("data-selection-border-top", "");
183
- }
184
-
185
- // Bottom border: if cell below is NOT selected or doesn't exist
186
- const cellBelow = `${rowIndex + 1},${columnIndex}`;
187
- if (!selectedPositions.has(cellBelow)) {
188
- element.setAttribute("data-selection-border-bottom", "");
189
- }
190
-
191
- // Left border: if cell to the left is NOT selected or doesn't exist
192
- const cellLeft = `${rowIndex},${columnIndex - 1}`;
193
- if (!selectedPositions.has(cellLeft)) {
194
- element.setAttribute("data-selection-border-left", "");
195
- }
196
-
197
- // Right border: if cell to the right is NOT selected or doesn't exist
198
- const cellRight = `${rowIndex},${columnIndex + 1}`;
199
- if (!selectedPositions.has(cellRight)) {
200
- element.setAttribute("data-selection-border-right", "");
201
- }
202
- });
203
- };
@@ -1,354 +0,0 @@
1
- // TODO: move this to @jsenv/dom (the initStickyGroup part, not the useLayoutEffect)
2
-
3
- import { createPubSub, setStyles } from "@jsenv/dom";
4
- import { useLayoutEffect } from "preact/hooks";
5
-
6
- // React hook version for easy integration
7
- export const useStickyGroup = (
8
- elementRef,
9
- { elementReceivingCumulativeStickyPositionRef, elementSelector } = {},
10
- ) => {
11
- useLayoutEffect(() => {
12
- const element = elementRef.current;
13
- if (!element) {
14
- return undefined;
15
- }
16
- return initStickyGroup(element, {
17
- elementSelector,
18
- elementReceivingCumulativeStickyPosition:
19
- elementReceivingCumulativeStickyPositionRef.current,
20
- });
21
- }, [elementSelector]);
22
- };
23
-
24
- const ITEM_LEFT_VAR = "--sticky-group-item-left";
25
- const ITEM_TOP_VAR = "--sticky-group-item-top";
26
- const FRONTIER_LEFT_VAR = "--sticky-group-left";
27
- const FRONTIER_TOP_VAR = "--sticky-group-top";
28
- // const FRONTIER_LEFT_VIEWPORT_VAR = "--sticky-group-left-viewport";
29
- // const FRONTIER_TOP_VIEWPORT_VAR = "--sticky-group-top-viewport";
30
-
31
- /**
32
- * Creates a sticky group that manages positioning for multiple sticky elements
33
- * that need to be aware of each other's dimensions.
34
- * Always uses CSS variables for positioning.
35
- *
36
- * @param {HTMLElement} container The container element
37
- * @returns {Function} Cleanup function
38
- */
39
- const initStickyGroup = (
40
- container,
41
- { elementSelector, elementReceivingCumulativeStickyPosition } = {},
42
- ) => {
43
- if (!container) {
44
- throw new Error("initStickyGroup: container is required");
45
- }
46
-
47
- const [teardown, addTeardown] = createPubSub();
48
- const [cleanup, addCleanup, clearCleanup] = createPubSub();
49
- addTeardown(cleanup);
50
-
51
- const element = elementSelector
52
- ? container.querySelector(elementSelector)
53
- : container;
54
- const isGrid =
55
- element.tagName === "TABLE" || element.classList.contains("navi_table");
56
- const updatePositions = () => {
57
- // Clear all previous CSS variable cleanups before setting new ones
58
- cleanup();
59
- clearCleanup();
60
-
61
- if (isGrid) {
62
- updateGridPositions();
63
- } else {
64
- updateLinearPositions();
65
- }
66
- };
67
-
68
- const updateGridPositions = () => {
69
- // Handle table grid - update both horizontal and vertical sticky elements
70
- updateTableColumns();
71
- updateTableRows();
72
- };
73
- const updateTableColumns = () => {
74
- // Find all sticky columns by checking all rows to identify which columns have sticky cells
75
- const allStickyColumnCells = element.querySelectorAll(
76
- ".navi_table_cell[data-sticky-left]",
77
- );
78
- if (allStickyColumnCells.length === 0) {
79
- return;
80
- }
81
-
82
- // Get the first row to determine column indices (use any row that exists)
83
- const firstRow = element.querySelector(".navi_tr");
84
- if (!firstRow) {
85
- return;
86
- }
87
-
88
- // Group sticky cells by column index
89
- const stickyColumnsByIndex = new Map();
90
- allStickyColumnCells.forEach((cell) => {
91
- const row = cell.closest(".navi_tr");
92
- const columnIndex = Array.from(row.children).indexOf(cell);
93
- if (!stickyColumnsByIndex.has(columnIndex)) {
94
- stickyColumnsByIndex.set(columnIndex, []);
95
- }
96
- stickyColumnsByIndex.get(columnIndex).push(cell);
97
- });
98
-
99
- // Sort columns by index and process them
100
- const sortedColumnIndices = Array.from(stickyColumnsByIndex.keys()).sort(
101
- (a, b) => a - b,
102
- );
103
- let cumulativeWidth = 0;
104
-
105
- sortedColumnIndices.forEach((columnIndex, stickyIndex) => {
106
- const cellsInColumn = stickyColumnsByIndex.get(columnIndex);
107
- const leftPosition = stickyIndex === 0 ? 0 : cumulativeWidth;
108
-
109
- // Set CSS variable on all sticky cells in this column using setStyles for proper cleanup
110
- cellsInColumn.forEach((cell) => {
111
- const restoreStyles = setStyles(cell, {
112
- [ITEM_LEFT_VAR]: `${leftPosition}px`,
113
- });
114
- addCleanup(restoreStyles);
115
- });
116
-
117
- // Also set CSS variable on corresponding <col> element if it exists
118
- const colgroup = element.querySelector(".navi_colgroup");
119
- if (colgroup) {
120
- const colElements = Array.from(colgroup.querySelectorAll(".navi_col"));
121
- const correspondingCol = colElements[columnIndex];
122
- if (correspondingCol) {
123
- const restoreStyles = setStyles(correspondingCol, {
124
- [ITEM_LEFT_VAR]: `${leftPosition}px`,
125
- });
126
- addCleanup(restoreStyles);
127
- }
128
- }
129
-
130
- // Update cumulative width for next column using the first cell in this column as reference
131
- const referenceCell = cellsInColumn[0];
132
- const columnWidth = referenceCell.getBoundingClientRect().width;
133
- if (stickyIndex === 0) {
134
- cumulativeWidth = columnWidth;
135
- } else {
136
- cumulativeWidth += columnWidth;
137
- }
138
- });
139
-
140
- // Set frontier variables with proper cleanup tracking
141
- const restoreContainerStyles = setStyles(container, {
142
- [FRONTIER_LEFT_VAR]: `${cumulativeWidth}px`,
143
- });
144
- addCleanup(restoreContainerStyles);
145
-
146
- if (elementReceivingCumulativeStickyPosition) {
147
- const restoreCumulativeStyles = setStyles(
148
- elementReceivingCumulativeStickyPosition,
149
- {
150
- [FRONTIER_LEFT_VAR]: `${cumulativeWidth}px`,
151
- },
152
- );
153
- addCleanup(restoreCumulativeStyles);
154
- }
155
- };
156
- const updateTableRows = () => {
157
- // Handle sticky rows by finding cells with data-sticky-top and grouping by row
158
- const stickyCells = element.querySelectorAll(
159
- ".navi_table_cell[data-sticky-top]",
160
- );
161
- if (stickyCells.length === 0) {
162
- return;
163
- }
164
-
165
- // Group cells by their parent row
166
- const rowsWithStickyCells = new Map();
167
- stickyCells.forEach((cell) => {
168
- const row = cell.parentElement;
169
- if (!rowsWithStickyCells.has(row)) {
170
- rowsWithStickyCells.set(row, []);
171
- }
172
- rowsWithStickyCells.get(row).push(cell);
173
- });
174
-
175
- // Convert to array and sort by row position in DOM
176
- const allRows = Array.from(element.querySelectorAll(".navi_tr"));
177
- const stickyRows = Array.from(rowsWithStickyCells.keys()).sort((a, b) => {
178
- const aIndex = allRows.indexOf(a);
179
- const bIndex = allRows.indexOf(b);
180
- return aIndex - bIndex;
181
- });
182
-
183
- let cumulativeHeight = 0;
184
- stickyRows.forEach((row, index) => {
185
- const rowCells = rowsWithStickyCells.get(row);
186
- const topPosition = index === 0 ? 0 : cumulativeHeight;
187
-
188
- // Set CSS variable on all sticky cells in this row using setStyles for proper cleanup
189
- rowCells.forEach((cell) => {
190
- const restoreStyles = setStyles(cell, {
191
- [ITEM_TOP_VAR]: `${topPosition}px`,
192
- });
193
- addCleanup(restoreStyles);
194
- });
195
-
196
- // Also set CSS variable on the <tr> element itself
197
- const restoreRowStyles = setStyles(row, {
198
- [ITEM_TOP_VAR]: `${topPosition}px`,
199
- });
200
- addCleanup(restoreRowStyles);
201
-
202
- // Update cumulative height for next row
203
- const rowHeight = row.getBoundingClientRect().height;
204
- if (index === 0) {
205
- cumulativeHeight = rowHeight;
206
- } else {
207
- cumulativeHeight += rowHeight;
208
- }
209
- });
210
-
211
- // Set frontier variables with proper cleanup tracking
212
- const restoreContainerStyles = setStyles(container, {
213
- [FRONTIER_TOP_VAR]: `${cumulativeHeight}px`,
214
- });
215
- addCleanup(restoreContainerStyles);
216
-
217
- if (elementReceivingCumulativeStickyPosition) {
218
- const restoreCumulativeStyles = setStyles(
219
- elementReceivingCumulativeStickyPosition,
220
- {
221
- [FRONTIER_TOP_VAR]: `${cumulativeHeight}px`,
222
- },
223
- );
224
- addCleanup(restoreCumulativeStyles);
225
- }
226
- };
227
-
228
- const updateLinearPositions = () => {
229
- // Handle linear container - detect direction from first sticky element
230
- const stickyElements = element.querySelectorAll(
231
- "[data-sticky-left], [data-sticky-top]",
232
- );
233
- if (stickyElements.length <= 1) return;
234
-
235
- const firstElement = stickyElements[0];
236
- const isHorizontal = firstElement.hasAttribute("data-sticky-left");
237
- const dimensionProperty = isHorizontal ? "width" : "height";
238
- const cssVariableName = isHorizontal ? ITEM_LEFT_VAR : ITEM_TOP_VAR;
239
-
240
- let cumulativeSize = 0;
241
- stickyElements.forEach((element, index) => {
242
- if (index === 0) {
243
- // First element stays at position 0
244
- const restoreStyles = setStyles(element, {
245
- [cssVariableName]: "0px",
246
- });
247
- addCleanup(restoreStyles);
248
- cumulativeSize = element.getBoundingClientRect()[dimensionProperty];
249
- } else {
250
- // Subsequent elements use cumulative positioning
251
- const position = cumulativeSize;
252
- const restoreStyles = setStyles(element, {
253
- [cssVariableName]: `${position}px`,
254
- });
255
- addCleanup(restoreStyles);
256
- cumulativeSize += element.getBoundingClientRect()[dimensionProperty];
257
- }
258
- });
259
-
260
- // Set frontier variables with proper cleanup tracking
261
- const frontierVar = isHorizontal ? FRONTIER_LEFT_VAR : FRONTIER_TOP_VAR;
262
- const restoreContainerStyles = setStyles(container, {
263
- [frontierVar]: `${cumulativeSize}px`,
264
- });
265
- addCleanup(restoreContainerStyles);
266
-
267
- if (elementReceivingCumulativeStickyPosition) {
268
- const restoreCumulativeStyles = setStyles(
269
- elementReceivingCumulativeStickyPosition,
270
- {
271
- [frontierVar]: `${cumulativeSize}px`,
272
- },
273
- );
274
- addCleanup(restoreCumulativeStyles);
275
- }
276
- };
277
-
278
- // Initial positioning
279
- updatePositions();
280
-
281
- // Set up ResizeObserver to handle size changes
282
- const resizeObserver = new ResizeObserver(() => {
283
- updatePositions();
284
- });
285
-
286
- // Set up MutationObserver to handle DOM changes
287
- const mutationObserver = new MutationObserver((mutations) => {
288
- let shouldUpdate = false;
289
-
290
- mutations.forEach((mutation) => {
291
- // Check if sticky elements were added/removed or attributes changed
292
- if (mutation.type === "childList") {
293
- shouldUpdate = true;
294
- }
295
- if (mutation.type === "attributes") {
296
- // Check if the mutation affects sticky attributes
297
- if (
298
- mutation.attributeName === "data-sticky-left" ||
299
- mutation.attributeName === "data-sticky-top"
300
- ) {
301
- shouldUpdate = true;
302
- }
303
- }
304
- });
305
-
306
- if (shouldUpdate) {
307
- updatePositions();
308
- }
309
- });
310
-
311
- // Start observing
312
- resizeObserver.observe(element);
313
- addTeardown(() => {
314
- resizeObserver.disconnect();
315
- });
316
-
317
- mutationObserver.observe(element, {
318
- attributes: true,
319
- childList: true,
320
- subtree: true,
321
- attributeFilter: ["data-sticky-left", "data-sticky-top"],
322
- });
323
- addTeardown(() => {
324
- mutationObserver.disconnect();
325
- });
326
-
327
- // Return cleanup function
328
- return () => {
329
- teardown();
330
- };
331
- };
332
-
333
- // const visualPositionEffect = (element, callback) => {
334
- // const updatePosition = () => {
335
- // const { left, top } = getVisualRect(element, document.body, {
336
- // isStickyLeft: false,
337
- // isStickyTop: false,
338
- // });
339
- // callback({ left, top });
340
- // };
341
- // updatePosition();
342
-
343
- // window.addEventListener("scroll", updatePosition, { passive: true });
344
- // window.addEventListener("resize", updatePosition);
345
- // window.addEventListener("touchmove", updatePosition);
346
-
347
- // return () => {
348
- // window.removeEventListener("scroll", updatePosition, {
349
- // passive: true,
350
- // });
351
- // window.removeEventListener("resize", updatePosition);
352
- // window.removeEventListener("touchmove", updatePosition);
353
- // };
354
- // };
@@ -1,25 +0,0 @@
1
- import { createContext } from "preact";
2
- import { useMemo } from "preact/hooks";
3
-
4
- import { useStableCallback } from "../../use_stable_callback.js";
5
-
6
- export const TableStickyContext = createContext();
7
-
8
- export const useTableStickyContextValue = ({
9
- stickyLeftFrontierColumnIndex,
10
- stickyTopFrontierRowIndex,
11
- onStickyLeftFrontierChange,
12
- onStickyTopFrontierChange,
13
- }) => {
14
- onStickyLeftFrontierChange = useStableCallback(onStickyLeftFrontierChange);
15
- onStickyTopFrontierChange = useStableCallback(onStickyTopFrontierChange);
16
-
17
- return useMemo(() => {
18
- return {
19
- stickyLeftFrontierColumnIndex,
20
- stickyTopFrontierRowIndex,
21
- onStickyLeftFrontierChange,
22
- onStickyTopFrontierChange,
23
- };
24
- }, [stickyLeftFrontierColumnIndex, stickyTopFrontierRowIndex]);
25
- };