@allxsmith/bestax-bulma 5.46.0 → 5.47.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -10138,6 +10138,38 @@ function parentItemOf(element) {
10138
10138
  const group = element.parentElement;
10139
10139
  return group.getAttribute('role') === 'group' ? group.parentElement : null;
10140
10140
  }
10141
+ /** No positions: on the server, and until the tree has mounted. */
10142
+ const NO_POSITIONS = new Map();
10143
+ /**
10144
+ * Each item's place among the items beside it, by its key, read off the
10145
+ * items on the page: the ones in the same group, or at the top. A loading or
10146
+ * failed load's row counts, as it does for a browser that works positions
10147
+ * out for itself, so the declared ones say what a browser would.
10148
+ */
10149
+ function positionsOf(shown, { values }) {
10150
+ const sets = new Map();
10151
+ shown.forEach(element => {
10152
+ const set = sets.get(element.parentElement);
10153
+ if (set)
10154
+ set.push(element);
10155
+ else
10156
+ sets.set(element.parentElement, [element]);
10157
+ });
10158
+ const positions = new Map();
10159
+ sets.forEach(set => set.forEach((element, at) => positions.set(values.get(element), {
10160
+ posinset: at + 1,
10161
+ setsize: set.length,
10162
+ })));
10163
+ return positions;
10164
+ }
10165
+ /** Whether two sets of positions say the same for every item. */
10166
+ function samePositions(a, b) {
10167
+ return (a.size === b.size &&
10168
+ Array.from(a).every(([key, { posinset, setsize }]) => {
10169
+ const other = b.get(key);
10170
+ return other?.posinset === posinset && other.setsize === setsize;
10171
+ }));
10172
+ }
10141
10173
  /** The values of the items an item on the page is in, from the top. */
10142
10174
  function pathOf(element, { values }) {
10143
10175
  const path = [];
@@ -10232,9 +10264,11 @@ function useControlled(controlled, initial) {
10232
10264
  return [isControlled ? controlled : own, set];
10233
10265
  }
10234
10266
  /**
10235
- * The open state, the selection, the keys, the tab stop and the registry of
10236
- * a TreeView (#1096). Focus moves for real, with a roving tabindex, rather
10237
- * than through `aria-activedescendant`.
10267
+ * The open state, the selection, the keys, the tab stop, the positions and
10268
+ * the registry of a TreeView (#1096). Focus moves for real, with a roving
10269
+ * tabindex, rather than through `aria-activedescendant`. Each item's
10270
+ * position among the items beside it is declared rather than left to the
10271
+ * browser (#1171), read off the page once the tree has mounted.
10238
10272
  *
10239
10273
  * Every item registers its value, its parent and whether it's disabled from
10240
10274
  * a layout effect. In a checkbox tree a closed item's children still render
@@ -10276,6 +10310,8 @@ function useTreeView(options) {
10276
10310
  const restingPathRef = React.useRef([]);
10277
10311
  // The values above an item that left the page with focus in it.
10278
10312
  const lostRef = React.useRef(null);
10313
+ // Each item's place among the items beside it, by its key.
10314
+ const [positions, setPositions] = React.useState(NO_POSITIONS);
10279
10315
  const { records } = registryRef.current;
10280
10316
  const checks = mode === 'checkbox'
10281
10317
  ? // eslint-disable-next-line react-hooks/refs -- read in render on purpose: items write the registry from their layout effects and then ask for this render with `refresh`
@@ -10329,11 +10365,12 @@ function useTreeView(options) {
10329
10365
  else
10330
10366
  act(item, list);
10331
10367
  };
10332
- // After every render, put the stop where it belongs. While focus is in
10333
- // the tree it's the focused item. Otherwise it's the selected item, or the
10334
- // first checked one on the page, and failing that it stays where it was.
10335
- // An item that goes away hands it to its nearest ancestor on the page, or
10336
- // to the first item, and focus goes with it if it had focus.
10368
+ // After every render, read each item's position off the page, and put the
10369
+ // stop where it belongs. While focus is in the tree it's the focused item.
10370
+ // Otherwise it's the selected item, or the first checked one on the page,
10371
+ // and failing that it stays where it was. An item that goes away hands it
10372
+ // to its nearest ancestor on the page, or to the first item, and focus goes
10373
+ // with it if it had focus.
10337
10374
  // eslint-disable-next-line react-hooks/exhaustive-deps -- after every render on purpose: it reads the page, and sets state only when what it reads has changed
10338
10375
  useClientLayoutEffect(() => {
10339
10376
  const tree = treeRef.current;
@@ -10343,6 +10380,9 @@ function useTreeView(options) {
10343
10380
  // sharing a value, which they shouldn't, can leave it without one the
10344
10381
  // page still has.
10345
10382
  const shown = shownIn(tree, registry);
10383
+ const placed = positionsOf(shown, registry);
10384
+ if (!samePositions(positions, placed))
10385
+ setPositions(placed);
10346
10386
  const onPage = shown.map(element => registry.values.get(element));
10347
10387
  const elementOf = (value) => shown[onPage.indexOf(value)];
10348
10388
  const isShown = (value) => value != null && onPage.includes(value);
@@ -10364,6 +10404,20 @@ function useTreeView(options) {
10364
10404
  restingPathRef.current = next ? pathOf(elementOf(next), registry) : [];
10365
10405
  }
10366
10406
  });
10407
+ // Items can also move without the tree rendering, such as children that a
10408
+ // component of the app sorts with state of its own. That changes no record,
10409
+ // so the page is watched for items coming, going and moving, and the
10410
+ // positions are read again.
10411
+ React.useEffect(() => {
10412
+ const tree = treeRef.current;
10413
+ const observer = new MutationObserver(() => {
10414
+ const registry = registryRef.current;
10415
+ const placed = positionsOf(shownIn(tree, registry), registry);
10416
+ setPositions(current => samePositions(current, placed) ? current : placed);
10417
+ });
10418
+ observer.observe(tree, { childList: true, subtree: true });
10419
+ return () => observer.disconnect();
10420
+ }, []);
10367
10421
  const register = React.useCallback((value, record) => {
10368
10422
  const { records: known } = registryRef.current;
10369
10423
  known.set(value, record);
@@ -10486,6 +10540,7 @@ function useTreeView(options) {
10486
10540
  tabStop,
10487
10541
  isOpen,
10488
10542
  stateOf,
10543
+ positionOf: key => positions.get(key),
10489
10544
  register,
10490
10545
  setElement,
10491
10546
  onFocus,
@@ -11071,7 +11126,9 @@ const TreeViewItem = React.forwardRef(function TreeViewItem({ value, label, icon
11071
11126
  if (branch.hidden)
11072
11127
  return jsxRuntime.jsx(jsxRuntime.Fragment, { children: items });
11073
11128
  const info = { value, parent, disabled, isParent, open };
11074
- return (jsxRuntime.jsxs("li", { ...props, ref: setRefs, className: classNames(itemClass, className), role: "treeitem", "aria-labelledby": labelId, "aria-level": branch.path.length + 1, "aria-expanded": isParent ? open : undefined, "aria-disabled": disabled || undefined, "aria-selected": selected, "aria-checked": checked, tabIndex: tree.tabStop === value ? 0 : -1, onKeyDown: event => {
11129
+ // A position the caller gives wins, for a set the page holds only part of.
11130
+ const position = tree.positionOf(value);
11131
+ return (jsxRuntime.jsxs("li", { ...props, ref: setRefs, className: classNames(itemClass, className), role: "treeitem", "aria-labelledby": labelId, "aria-level": branch.path.length + 1, "aria-posinset": props['aria-posinset'] ?? position?.posinset, "aria-setsize": props['aria-setsize'] ?? position?.setsize, "aria-expanded": isParent ? open : undefined, "aria-disabled": disabled || undefined, "aria-selected": selected, "aria-checked": checked, tabIndex: tree.tabStop === value ? 0 : -1, onKeyDown: event => {
11075
11132
  onKeyDown?.(event);
11076
11133
  tree.onKeyDown(event, info);
11077
11134
  }, onFocus: event => {
@@ -11141,7 +11198,8 @@ function StatusRow({ kind, parent, level, message, onRetry, labelRef, }) {
11141
11198
  open: false,
11142
11199
  retry,
11143
11200
  };
11144
- return (jsxRuntime.jsx("li", { ref: setRef, className: itemClass, role: "treeitem", "aria-labelledby": retry ? `${labelId} ${retryId}` : labelId, "aria-level": level, "aria-disabled": !retry || undefined, tabIndex: tree.tabStop === labelId ? 0 : -1, onKeyDown: event => tree.onKeyDown(event, info), onFocus: event => {
11201
+ const position = tree.positionOf(labelId);
11202
+ return (jsxRuntime.jsx("li", { ref: setRef, className: itemClass, role: "treeitem", "aria-labelledby": retry ? `${labelId} ${retryId}` : labelId, "aria-level": level, "aria-posinset": position?.posinset, "aria-setsize": position?.setsize, "aria-disabled": !retry || undefined, tabIndex: tree.tabStop === labelId ? 0 : -1, onKeyDown: event => tree.onKeyDown(event, info), onFocus: event => {
11145
11203
  // Focus in something the message holds isn't the row's.
11146
11204
  if (event.target === event.currentTarget) {
11147
11205
  tree.onFocus(labelId, event.currentTarget);