@jirawatpyk/aura-react 5.14.0 → 5.16.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.
@@ -52,6 +52,11 @@ const AppShell = React.forwardRef(function AppShell2(props, ref) {
52
52
  onNav(id);
53
53
  setOpen(false);
54
54
  },
55
+ /* 5.16: an action row (Sign out) closes the drawer too. */
56
+ onAction: function(id) {
57
+ setOpen(false);
58
+ if (navEl.props.onAction) navEl.props.onAction(id);
59
+ },
55
60
  className: cx(navEl.props.className, "is-in-drawer"),
56
61
  collapsed: false,
57
62
  collapsible: false
@@ -8,13 +8,83 @@ const Breadcrumb = React.forwardRef(function Breadcrumb2(props, ref) {
8
8
  const t = useStrings();
9
9
  const Link = useLinkComponent(props.linkComponent);
10
10
  const items = props.items || [];
11
- return /* @__PURE__ */ React.createElement("nav", { ref, "aria-label": props.label || t.breadcrumb, className: cx("aura-crumbs", props.className) }, /* @__PURE__ */ React.createElement("ol", null, items.map(function(it, i) {
11
+ const collapsible = !!props.collapseBelow && items.length > 2;
12
+ const trail = items.map(function(it) {
13
+ return it.label + "" + (it.href || "");
14
+ }).join("");
15
+ const st = React.useState(null), expanded = st[0] === trail, setExpanded = function() {
16
+ st[1](trail);
17
+ };
18
+ const listRef = React.useRef(null);
19
+ const focusNext = React.useRef(false);
20
+ React.useEffect(
21
+ function() {
22
+ if (!expanded || !focusNext.current) return;
23
+ focusNext.current = false;
24
+ const ol = listRef.current;
25
+ if (!ol) return;
26
+ let target = ol.querySelector(".aura-crumbs__middle a, .aura-crumbs__middle button");
27
+ if (!target) {
28
+ target = ol.querySelector(".aura-crumbs__middle > *");
29
+ if (target) target.tabIndex = -1;
30
+ }
31
+ if (target) target.focus();
32
+ },
33
+ [expanded]
34
+ );
35
+ const sep = /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconChevronRight, null), size: 12, className: "aura-crumbs__sep" });
36
+ const more = collapsible && !expanded ? /* @__PURE__ */ React.createElement("li", { key: "more", className: "aura-crumbs__more" }, /* @__PURE__ */ React.createElement(
37
+ "button",
38
+ {
39
+ type: "button",
40
+ "aria-label": t.breadcrumbMore,
41
+ onClick: function() {
42
+ focusNext.current = true;
43
+ setExpanded();
44
+ }
45
+ },
46
+ "\u2026"
47
+ ), sep) : null;
48
+ const out = [];
49
+ items.forEach(function(it, i) {
12
50
  const last = i === items.length - 1;
13
- return /* @__PURE__ */ React.createElement("li", { key: i }, last ? /* @__PURE__ */ React.createElement("span", { "aria-current": "page", className: "aura-crumbs__current" }, it.label) : it.href ? /* @__PURE__ */ React.createElement(Link, { href: it.href, onClick: it.onClick }, it.label) : it.onClick ? /* @__PURE__ */ React.createElement("button", { type: "button", onClick: it.onClick }, it.label) : (
14
- /* 5.7: a segment with no page and no action is text, not a button that does nothing. */
15
- /* @__PURE__ */ React.createElement("span", { className: "aura-crumbs__text" }, it.label)
16
- ), last ? null : /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconChevronRight, null), size: 12, className: "aura-crumbs__sep" }));
17
- })));
51
+ const lp = it.linkProps || {};
52
+ const plain = lp;
53
+ const own = lp.onClick;
54
+ const click = own || it.onClick ? function(e) {
55
+ if (own) own(e);
56
+ if (it.onClick) it.onClick();
57
+ } : void 0;
58
+ out.push(
59
+ /* @__PURE__ */ React.createElement(
60
+ "li",
61
+ {
62
+ key: i,
63
+ ...it.itemProps,
64
+ className: cx(it.itemProps && it.itemProps.className, collapsible && !last && i > 0 && "aura-crumbs__middle") || void 0
65
+ },
66
+ last ? /* @__PURE__ */ React.createElement("span", { ...plain, "aria-current": "page", className: cx("aura-crumbs__current", lp.className) }, it.label) : it.href ? /* @__PURE__ */ React.createElement(Link, { ...lp, href: it.href, onClick: click }, it.label) : it.onClick ? /* @__PURE__ */ React.createElement("button", { ...lp, type: "button", onClick: click }, it.label) : (
67
+ /* 5.7: a segment with no page and no action is text, not a button that does nothing. */
68
+ /* @__PURE__ */ React.createElement("span", { ...plain, className: cx("aura-crumbs__text", lp.className) }, it.label)
69
+ ),
70
+ last ? null : sep
71
+ )
72
+ );
73
+ if (i === 0 && more) out.push(more);
74
+ });
75
+ return /* @__PURE__ */ React.createElement(
76
+ "nav",
77
+ {
78
+ ref,
79
+ "aria-label": props.label || t.breadcrumb,
80
+ className: cx(
81
+ "aura-crumbs",
82
+ collapsible && !expanded && "aura-crumbs--collapse-" + props.collapseBelow,
83
+ props.className
84
+ )
85
+ },
86
+ /* @__PURE__ */ React.createElement("ol", { ref: listRef }, out)
87
+ );
18
88
  });
19
89
  export {
20
90
  Breadcrumb
@@ -18,7 +18,8 @@ function ButtonLink(props, ref, Link) {
18
18
  "linkComponent",
19
19
  "href",
20
20
  "fullWidth",
21
- "size"
21
+ "size",
22
+ "touchHeight"
22
23
  ]);
23
24
  const Tag = disabled ? "a" : Link;
24
25
  return /* @__PURE__ */ React.createElement(
@@ -34,6 +35,7 @@ function ButtonLink(props, ref, Link) {
34
35
  variant,
35
36
  size: props.size,
36
37
  fullWidth: props.fullWidth,
38
+ touchHeight: props.touchHeight,
37
39
  className: props.className
38
40
  }),
39
41
  onClick: disabled ? void 0 : props.onClick
@@ -64,7 +66,8 @@ const Button = React.forwardRef(
64
66
  "loading",
65
67
  "onClick",
66
68
  "fullWidth",
67
- "size"
69
+ "size",
70
+ "touchHeight"
68
71
  ]);
69
72
  return /* @__PURE__ */ React.createElement(
70
73
  "button",
@@ -76,6 +79,7 @@ const Button = React.forwardRef(
76
79
  variant,
77
80
  size: props.size,
78
81
  fullWidth: props.fullWidth,
82
+ touchHeight: props.touchHeight,
79
83
  loading,
80
84
  className: props.className
81
85
  }),
@@ -22,7 +22,8 @@ const Checkbox = React.forwardRef(function Checkbox2(props, ref) {
22
22
  "description",
23
23
  "className",
24
24
  "tabIndex",
25
- "disabled"
25
+ "disabled",
26
+ "hitArea"
26
27
  ]);
27
28
  const text = props.children;
28
29
  const labelled = text != null && text !== "";
@@ -32,10 +33,19 @@ const Checkbox = React.forwardRef(function Checkbox2(props, ref) {
32
33
  "Checkbox `label` is only the accessible name: it is not shown. In 6.0 it will be shown beside the box, like Switch and TextField. For visible text now pass it as children; for a bare box (a table row) add `hideLabel`."
33
34
  );
34
35
  const descId = props.description ? (props.id || auto) + "-desc" : void 0;
36
+ const ha = props.hitArea;
37
+ const hit = !labelled && ha && ha !== "box" ? ha === "target" ? { x: 4, y: 4 } : ha : null;
35
38
  return /* @__PURE__ */ React.createElement(
36
39
  "label",
37
40
  {
38
- className: cx("aura-check", labelled && "aura-check--labelled", props.disabled && "is-disabled", props.className),
41
+ className: cx(
42
+ "aura-check",
43
+ labelled && "aura-check--labelled",
44
+ hit && "has-hit",
45
+ props.disabled && "is-disabled",
46
+ props.className
47
+ ),
48
+ style: hit ? { "--aura-check-hit-x": hit.x + "px", "--aura-check-hit-y": hit.y + "px" } : void 0,
39
49
  onClick: function(e) {
40
50
  e.stopPropagation();
41
51
  }
@@ -28,6 +28,13 @@ const Combobox = React.forwardRef(
28
28
  const density = useDensity();
29
29
  const auto = uid(), id = props.id || auto, listId = id + "-list";
30
30
  const options = (props.options || []).map(toOpt);
31
+ const groups = props.groups || [];
32
+ groups.forEach(function(g, gi) {
33
+ g.options.forEach(function(o) {
34
+ options.push(Object.assign({}, toOpt(o), { __g: gi }));
35
+ });
36
+ });
37
+ const custom = !multi && !!props.allowCustomValue;
31
38
  const st = useMaybeControlled(
32
39
  multi ? mp.value : props.value === void 0 ? void 0 : props.value == null ? [] : [props.value],
33
40
  multi ? mp.defaultValue || [] : props.defaultValue == null ? [] : [props.defaultValue],
@@ -67,7 +74,9 @@ const Combobox = React.forwardRef(
67
74
  });
68
75
  const more = shown.length > limit;
69
76
  shown = shown.slice(0, limit);
70
- const activeIdx = Math.min(active, shown.length - 1);
77
+ const activeIdx = active < 0 ? query ? shown.findIndex(function(o) {
78
+ return !o.disabled && norm(o.label) === norm(query.trim());
79
+ }) : -1 : Math.min(active, shown.length - 1);
71
80
  function place() {
72
81
  if (!boxRef.current) return;
73
82
  const r = boxRef.current.getBoundingClientRect();
@@ -92,6 +101,7 @@ const Combobox = React.forwardRef(
92
101
  function outside(e) {
93
102
  if (boxRef.current && boxRef.current.contains(e.target)) return;
94
103
  if (listRef.current && listRef.current.contains(e.target)) return;
104
+ if (commitRef.current()) return;
95
105
  close(false);
96
106
  }
97
107
  function onScroll(e) {
@@ -128,6 +138,27 @@ const Combobox = React.forwardRef(
128
138
  setOpen(false);
129
139
  setQuery(null);
130
140
  }
141
+ const committed = React.useRef(false);
142
+ const commitRef = React.useRef(function() {
143
+ return false;
144
+ });
145
+ function commitTyped() {
146
+ if (!custom || query == null || committed.current) return false;
147
+ committed.current = true;
148
+ const q = query.trim();
149
+ if (!q) {
150
+ if (props.clearable !== false) setValue(null);
151
+ } else {
152
+ const hit = options.filter(function(o) {
153
+ return !o.disabled && norm(o.label) === norm(q);
154
+ })[0];
155
+ setValue(hit ? hit.value : q);
156
+ }
157
+ setQuery(null);
158
+ setOpen(false);
159
+ return true;
160
+ }
161
+ commitRef.current = commitTyped;
131
162
  function choose(o) {
132
163
  if (!o || o.disabled) return;
133
164
  if (multi) {
@@ -168,7 +199,7 @@ const Combobox = React.forwardRef(
168
199
  if (open && live[activeIdx]) {
169
200
  e.preventDefault();
170
201
  choose(live[activeIdx]);
171
- }
202
+ } else if (commitTyped()) e.preventDefault();
172
203
  } else if (k === "Escape") {
173
204
  if (open) {
174
205
  e.preventDefault();
@@ -180,58 +211,95 @@ const Combobox = React.forwardRef(
180
211
  } else if (k === "Backspace" && multi && !props.readOnly && !text && values.length) {
181
212
  setValues(values.slice(0, -1));
182
213
  } else if (k === "Tab") {
214
+ if (custom && open && query != null && live[activeIdx]) {
215
+ choose(live[activeIdx]);
216
+ return;
217
+ }
218
+ if (commitTyped()) return;
183
219
  if (open) close();
184
220
  }
185
221
  }
186
- const text = query != null ? query : selected ? selected.label : "";
222
+ const text = query != null ? query : selected ? selected.label : custom && value != null ? value : "";
187
223
  const summaryId = id + "-picked";
188
224
  const optId = function(i) {
189
225
  return id + "-opt-" + i;
190
226
  };
227
+ function renderOpt(o, i) {
228
+ const isSel = multi ? isPicked(o.value) : !!selected && o.value === selected.value;
229
+ const blocked = o.disabled || multi && full && !isSel;
230
+ return /* @__PURE__ */ React.createElement(
231
+ "li",
232
+ {
233
+ key: o.value,
234
+ id: optId(i),
235
+ role: "option",
236
+ "data-idx": i,
237
+ "aria-selected": isSel,
238
+ "aria-disabled": blocked || void 0,
239
+ className: cx(
240
+ "aura-combo__option",
241
+ i === activeIdx && "is-active",
242
+ isSel && "is-selected",
243
+ blocked && "is-disabled"
244
+ ),
245
+ onPointerDown: function(e) {
246
+ e.preventDefault();
247
+ },
248
+ onClick: function() {
249
+ if (!blocked || isSel) choose(o);
250
+ },
251
+ onPointerMove: function() {
252
+ if (activeIdx !== i) setActive(i);
253
+ }
254
+ },
255
+ o.icon ? /* @__PURE__ */ React.createElement(Icon, { name: o.icon }) : null,
256
+ /* @__PURE__ */ React.createElement("span", { className: "aura-combo__text" }, /* @__PURE__ */ React.createElement("span", { className: "aura-combo__label" }, o.label), o.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-combo__desc" }, o.description) : null),
257
+ isSel ? /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconCheck, null), className: "aura-combo__check" }) : null
258
+ );
259
+ }
260
+ function grouped(list2) {
261
+ const out = [];
262
+ list2.forEach(function(o, i) {
263
+ const last = out[out.length - 1];
264
+ if (last && last.g === o.__g) last.items.push([o, i]);
265
+ else out.push({ g: o.__g, items: [[o, i]] });
266
+ });
267
+ return out;
268
+ }
191
269
  const list = open && mounted && posState[0] ? createPortal(
192
- /* @__PURE__ */ React.createElement("div", { ref: listRef, "data-density": density, className: "aura-combo__popover", style: posState[0] }, live.length ? /* @__PURE__ */ React.createElement(
193
- "ul",
270
+ /* @__PURE__ */ React.createElement(
271
+ "div",
194
272
  {
195
- id: listId,
196
- role: "listbox",
197
- "aria-label": props.label,
198
- "aria-multiselectable": multi || void 0,
199
- className: "aura-combo__list"
273
+ ref: listRef,
274
+ "data-density": density,
275
+ className: "aura-combo__popover",
276
+ style: posState[0],
277
+ onPointerDown: function(e) {
278
+ e.preventDefault();
279
+ }
200
280
  },
201
- live.map(function(o, i) {
202
- const isSel = multi ? isPicked(o.value) : !!selected && o.value === selected.value;
203
- const blocked = o.disabled || multi && full && !isSel;
204
- return /* @__PURE__ */ React.createElement(
205
- "li",
206
- {
207
- key: o.value,
208
- id: optId(i),
209
- role: "option",
210
- "data-idx": i,
211
- "aria-selected": isSel,
212
- "aria-disabled": blocked || void 0,
213
- className: cx(
214
- "aura-combo__option",
215
- i === activeIdx && "is-active",
216
- isSel && "is-selected",
217
- blocked && "is-disabled"
218
- ),
219
- onPointerDown: function(e) {
220
- e.preventDefault();
221
- },
222
- onClick: function() {
223
- if (!blocked || isSel) choose(o);
224
- },
225
- onPointerMove: function() {
226
- if (activeIdx !== i) setActive(i);
227
- }
228
- },
229
- o.icon ? /* @__PURE__ */ React.createElement(Icon, { name: o.icon }) : null,
230
- /* @__PURE__ */ React.createElement("span", { className: "aura-combo__text" }, /* @__PURE__ */ React.createElement("span", { className: "aura-combo__label" }, o.label), o.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-combo__desc" }, o.description) : null),
231
- isSel ? /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconCheck, null), className: "aura-combo__check" }) : null
232
- );
233
- })
234
- ) : null, props.loading || !live.length || more ? /* @__PURE__ */ React.createElement("div", { className: "aura-combo__list", role: "status" }, props.loading ? /* @__PURE__ */ React.createElement("div", { className: "aura-combo__note" }, /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconLoaderCircle, null), className: "aura-spin" }), props.loadingText || t.searching) : !live.length ? /* @__PURE__ */ React.createElement("div", { className: "aura-combo__note" }, props.emptyText || t.noMatches) : /* @__PURE__ */ React.createElement("div", { className: "aura-combo__note" }, t.keepTyping((props.options || []).length))) : null),
281
+ live.length ? /* @__PURE__ */ React.createElement(
282
+ "ul",
283
+ {
284
+ id: listId,
285
+ role: "listbox",
286
+ "aria-label": props.label,
287
+ "aria-multiselectable": multi || void 0,
288
+ className: "aura-combo__list"
289
+ },
290
+ grouped(live).map(function(seg) {
291
+ if (seg.g == null)
292
+ return /* @__PURE__ */ React.createElement(React.Fragment, { key: "u" + seg.items[0][1] }, seg.items.map(function(p) {
293
+ return renderOpt(p[0], p[1]);
294
+ }));
295
+ const gid = id + "-group-" + seg.g + "-" + seg.items[0][1];
296
+ return /* @__PURE__ */ React.createElement("li", { key: gid, role: "group", "aria-labelledby": gid, className: "aura-combo__group" }, /* @__PURE__ */ React.createElement("span", { id: gid, role: "presentation", className: "aura-combo__group-label" }, groups[seg.g].label), /* @__PURE__ */ React.createElement("ul", { role: "none", className: "aura-combo__grouplist" }, seg.items.map(function(p) {
297
+ return renderOpt(p[0], p[1]);
298
+ })));
299
+ })
300
+ ) : null,
301
+ props.loading || !live.length || more ? /* @__PURE__ */ React.createElement("div", { className: "aura-combo__list", role: "status" }, props.loading ? /* @__PURE__ */ React.createElement("div", { className: "aura-combo__note" }, /* @__PURE__ */ React.createElement(Icon, { name: /* @__PURE__ */ React.createElement(IconLoaderCircle, null), className: "aura-spin" }), props.loadingText || t.searching) : !live.length ? /* @__PURE__ */ React.createElement("div", { className: "aura-combo__note" }, props.emptyText || t.noMatches) : /* @__PURE__ */ React.createElement("div", { className: "aura-combo__note" }, t.keepTyping(options.length))) : null
302
+ ),
235
303
  document.body
236
304
  ) : null;
237
305
  return /* @__PURE__ */ React.createElement(
@@ -293,8 +361,9 @@ const Combobox = React.forwardRef(
293
361
  required: props.required && (!multi || !values.length),
294
362
  value: text,
295
363
  onChange: function(e) {
364
+ committed.current = false;
296
365
  setQuery(e.target.value);
297
- setActive(0);
366
+ setActive(custom ? -1 : 0);
298
367
  if (!open) setOpen(true);
299
368
  if (props.onSearch) props.onSearch(e.target.value);
300
369
  },
@@ -303,6 +372,8 @@ const Combobox = React.forwardRef(
303
372
  onBlur: function() {
304
373
  setTimeout(function() {
305
374
  if (listRef.current && listRef.current.contains(document.activeElement)) return;
375
+ if (custom && document.activeElement === inputRef.current) return;
376
+ if (commitRef.current()) return;
306
377
  setOpen(false);
307
378
  setQuery(null);
308
379
  }, 0);
@@ -67,7 +67,9 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
67
67
  oneCallback ? null : props.onSortChange
68
68
  );
69
69
  const sort = sortState[0], setSort = sortState[1];
70
- const selState = useMaybeControlled(props.selected, props.defaultSelected || [], props.onSelectionChange);
70
+ const selState = useMaybeControlled(props.selected, props.defaultSelected || [], null);
71
+ const onSel = React.useRef(props.onSelectionChange);
72
+ onSel.current = props.onSelectionChange;
71
73
  const canSelect = function(r) {
72
74
  return !!r && (!props.isRowSelectable || !!props.isRowSelectable(r));
73
75
  };
@@ -78,7 +80,11 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
78
80
  });
79
81
  const selected = selState[0].filter(function(k) {
80
82
  return !rejected[String(k)];
81
- }), setSelected = selState[1];
83
+ });
84
+ function setSelected(next, change) {
85
+ selState[1](next);
86
+ if (onSel.current) onSel.current(next, change || { key: null, shiftKey: false, source: "sync" });
87
+ }
82
88
  const droppedSig = selState[0].length === selected.length ? "" : "#" + selState[0].filter(function(k) {
83
89
  return rejected[String(k)];
84
90
  }).join("\0") + "#" + selected.join("\0");
@@ -566,11 +572,39 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
566
572
  return selSet[String(k)];
567
573
  }).length;
568
574
  const all = nSel > 0 && nSel === pageKeys.length;
569
- function toggle(k, on) {
575
+ const anchor = React.useRef(null);
576
+ const shiftClick = React.useRef(false);
577
+ function toggle(k, on, how) {
578
+ const same = function(x) {
579
+ return String(x) === String(k);
580
+ };
581
+ let range = null;
582
+ if (props.rangeSelect && how.shiftKey && anchor.current != null) {
583
+ const from = pageKeys.findIndex(function(x) {
584
+ return String(x) === String(anchor.current);
585
+ });
586
+ const to = pageKeys.findIndex(same);
587
+ if (from >= 0 && to >= 0) range = pageKeys.slice(Math.min(from, to), Math.max(from, to) + 1);
588
+ }
589
+ anchor.current = k;
590
+ const change = { key: k, shiftKey: how.shiftKey, source: how.source };
591
+ if (range) {
592
+ const inRange = {};
593
+ range.forEach(function(x) {
594
+ inRange[String(x)] = true;
595
+ });
596
+ const rest = selected.filter(function(x) {
597
+ return !inRange[String(x)];
598
+ });
599
+ change.range = range;
600
+ setSelected(on ? rest.concat(range) : rest, change);
601
+ return;
602
+ }
570
603
  setSelected(
571
604
  on ? selected.concat([k]) : selected.filter(function(x) {
572
- return String(x) !== String(k);
573
- })
605
+ return !same(x);
606
+ }),
607
+ change
574
608
  );
575
609
  }
576
610
  function toggleAll() {
@@ -583,7 +617,8 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
583
617
  pageKeys.filter(function(k) {
584
618
  return !selSet[String(k)];
585
619
  })
586
- )
620
+ ),
621
+ { key: null, shiftKey: false, source: "all" }
587
622
  );
588
623
  }
589
624
  function canResize(c) {
@@ -762,8 +797,8 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
762
797
  };
763
798
  }).concat([{ separator: true }, { label: t.resetColumns, icon: /* @__PURE__ */ React.createElement(IconRotateCcw, null), onSelect: resetColumns }]);
764
799
  }
765
- function openMenu(kind, key, anchor, rc) {
766
- setMenu({ kind, key, anchor, rc });
800
+ function openMenu(kind, key, anchor2, rc) {
801
+ setMenu({ kind, key, anchor: anchor2, rc });
767
802
  }
768
803
  function closeMenu(restore) {
769
804
  const m = menu;
@@ -823,7 +858,8 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
823
858
  if (pageKeys.length) toggleAll();
824
859
  } else if (r === 0 && col && col.sortable && canSortAny) sortBy(col.key);
825
860
  else if (r > 0 && k === " " && selectable) {
826
- if (canSelect(row) && !(stacked && noCardSel)) toggle(row[rowKey], !selSet[row[rowKey]]);
861
+ if (canSelect(row) && !(stacked && noCardSel))
862
+ toggle(row[rowKey], !selSet[row[rowKey]], { shiftKey: e.shiftKey, source: "keyboard" });
827
863
  } else if (r > 0 && k === "Enter" && rowHref && !target.querySelector("button, a[href]:not(.aura-table__row-link), input, select, textarea"))
828
864
  followRow(target.closest('[role="row"]'), e);
829
865
  else if (r > 0 && k === "Enter" && target.querySelector("button, a[href], input, select, textarea"))
@@ -1013,6 +1049,9 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
1013
1049
  "aria-label": canSelect(r) ? selLabel : props.rowSelectDisabledLabel ? props.rowSelectDisabledLabel(r) : void 0,
1014
1050
  onFocus: function(e) {
1015
1051
  if (e.target === e.currentTarget) activeState[1]({ r: ri, c: 0 });
1052
+ },
1053
+ onClickCapture: function(e) {
1054
+ shiftClick.current = e.shiftKey;
1016
1055
  }
1017
1056
  },
1018
1057
  canSelect(r) ? /* @__PURE__ */ React.createElement(
@@ -1023,7 +1062,9 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
1023
1062
  tabIndex: -1,
1024
1063
  label: selLabel,
1025
1064
  onChange: function(on) {
1026
- toggle(k, on);
1065
+ const shift = shiftClick.current;
1066
+ shiftClick.current = false;
1067
+ toggle(k, on, { shiftKey: shift, source: "click" });
1027
1068
  }
1028
1069
  }
1029
1070
  ) : null
@@ -2,26 +2,81 @@
2
2
  import * as React from "react";
3
3
  import { useStrings, useDensity } from "./locale.js";
4
4
  import { createPortal } from "react-dom";
5
- import { cx, omit, uid, useMergedRef } from "./internal.js";
5
+ import { cx, devWarnOnce, omit, uid, useMaybeControlled, useMergedRef } from "./internal.js";
6
6
  import { IconButton } from "./IconButton.js";
7
7
  import { useModal } from "./useModal.js";
8
8
  import { IconX } from "./icons.js";
9
+ const DialogClose = React.createContext(null);
10
+ function useDialogClose() {
11
+ return React.useContext(DialogClose) || function() {
12
+ };
13
+ }
9
14
  const Dialog = React.forwardRef(function Dialog2(props, ref) {
10
15
  const t = useStrings();
11
16
  const density = useDensity();
12
17
  const own = React.useRef(null), merged = useMergedRef(ref, own), titleId = uid(), descId = uid();
18
+ const openState = useMaybeControlled(props.open, false, null), open = openState[0];
19
+ if (props.open === void 0 && !props.trigger)
20
+ devWarnOnce("dialog-no-open", "Dialog has neither `open` nor `trigger`, so it can never open. Pass one of them.");
21
+ const trigId = uid();
22
+ function closeNow() {
23
+ if (props.open === void 0) openState[1](false);
24
+ if (props.onClose) props.onClose();
25
+ }
13
26
  function close() {
14
- if (props.dismissible !== false && props.onClose) props.onClose();
27
+ if (props.dismissible === false) return;
28
+ closeNow();
15
29
  }
16
30
  const scrimCloses = props.dismissible !== false && (props.dismissOnScrim !== void 0 ? props.dismissOnScrim : props.role !== "alertdialog");
17
- const modal = useModal(props.open, own, {
31
+ const modal = useModal(open, own, {
18
32
  autoFocus: props.autoFocus,
19
33
  onEscape: close,
20
34
  bodySelector: ".aura-dialog__body",
21
- footSelector: ".aura-dialog__foot"
35
+ footSelector: ".aura-dialog__foot",
36
+ /* With a trigger, focus goes back to it even where a click doesn't focus buttons (Safari). */
37
+ finalFocus: props.trigger ? function() {
38
+ const ff = props.finalFocus;
39
+ const to = ff ? typeof ff === "function" ? ff() : ff.current : null;
40
+ return to || document.querySelector('[data-aura-trigger="' + trigId + '"]');
41
+ } : props.finalFocus,
42
+ onCloseComplete: props.onCloseComplete
22
43
  });
23
- if (!modal.ready) return null;
24
- return createPortal(
44
+ const trig = props.trigger;
45
+ const trigger = trig && React.isValidElement(trig) ? React.cloneElement(trig, {
46
+ "aria-haspopup": "dialog",
47
+ "aria-expanded": open,
48
+ "data-aura-trigger": trigId,
49
+ onClick: function(e) {
50
+ const own2 = trig.props.onClick;
51
+ if (own2) own2(e);
52
+ if (e.defaultPrevented) return;
53
+ if (props.open === void 0) openState[1](true);
54
+ if (props.onOpen) props.onOpen();
55
+ }
56
+ }) : null;
57
+ if (!modal.ready) return trigger;
58
+ const rest = omit(props, [
59
+ "open",
60
+ "onClose",
61
+ "title",
62
+ "description",
63
+ "children",
64
+ "footer",
65
+ "size",
66
+ "dismissible",
67
+ "dismissOnScrim",
68
+ "role",
69
+ "autoFocus",
70
+ "className",
71
+ "trigger",
72
+ "onOpen",
73
+ "finalFocus",
74
+ "onCloseComplete",
75
+ "aria-labelledby",
76
+ "aria-describedby",
77
+ "aria-modal"
78
+ ]);
79
+ const panel = createPortal(
25
80
  /* @__PURE__ */ React.createElement("div", { className: "aura-dialog-layer", "data-density": density, onKeyDown: modal.onKeyDown }, /* @__PURE__ */ React.createElement(
26
81
  "div",
27
82
  {
@@ -35,21 +90,22 @@ const Dialog = React.forwardRef(function Dialog2(props, ref) {
35
90
  ), /* @__PURE__ */ React.createElement(
36
91
  "div",
37
92
  {
93
+ ...rest,
38
94
  ref: merged,
39
95
  role: props.role || "dialog",
40
96
  "aria-modal": true,
41
97
  "aria-labelledby": titleId,
42
- "aria-describedby": props.description ? descId : void 0,
98
+ "aria-describedby": cx(props.description ? descId : "", props["aria-describedby"]) || void 0,
43
99
  tabIndex: -1,
44
100
  className: cx("aura-dialog", "aura-dialog--" + (props.size || "md"), props.className)
45
101
  },
46
102
  /* @__PURE__ */ React.createElement("div", { className: "aura-dialog__head" }, /* @__PURE__ */ React.createElement("h2", { className: "aura-dialog__title", id: titleId }, props.title), props.dismissible !== false ? /* @__PURE__ */ React.createElement(IconButton, { icon: /* @__PURE__ */ React.createElement(IconX, null), label: t.close, className: "aura-dialog__close", onClick: close }) : null),
47
103
  props.description ? /* @__PURE__ */ React.createElement("p", { className: "aura-dialog__desc", id: descId }, props.description) : null,
48
- props.children ? /* @__PURE__ */ React.createElement("div", { className: "aura-dialog__body" }, props.children) : null,
49
- props.footer ? /* @__PURE__ */ React.createElement("div", { className: "aura-dialog__foot" }, props.footer) : null
104
+ /* @__PURE__ */ React.createElement(DialogClose.Provider, { value: closeNow }, props.children ? /* @__PURE__ */ React.createElement("div", { className: "aura-dialog__body" }, props.children) : null, props.footer ? /* @__PURE__ */ React.createElement("div", { className: "aura-dialog__foot" }, props.footer) : null)
50
105
  )),
51
106
  document.body
52
107
  );
108
+ return trigger ? /* @__PURE__ */ React.createElement(React.Fragment, null, trigger, panel) : panel;
53
109
  });
54
110
  const Drawer = React.forwardRef(function Drawer2(props, ref) {
55
111
  const t = useStrings();
@@ -62,7 +118,9 @@ const Drawer = React.forwardRef(function Drawer2(props, ref) {
62
118
  autoFocus: props.autoFocus,
63
119
  onEscape: close,
64
120
  bodySelector: ".aura-drawer__body",
65
- footSelector: ".aura-drawer__foot"
121
+ footSelector: ".aura-drawer__foot",
122
+ finalFocus: props.finalFocus,
123
+ onCloseComplete: props.onCloseComplete
66
124
  });
67
125
  if (!modal.ready) return null;
68
126
  const side = props.side === "left" ? "left" : "right";
@@ -83,7 +141,9 @@ const Drawer = React.forwardRef(function Drawer2(props, ref) {
83
141
  "aria-labelledby",
84
142
  "className",
85
143
  "closeLabel",
86
- "closeProps"
144
+ "closeProps",
145
+ "finalFocus",
146
+ "onCloseComplete"
87
147
  ]);
88
148
  const drawerScrimCloses = props.dismissible !== false && props.dismissOnScrim !== false;
89
149
  return createPortal(
@@ -132,5 +192,6 @@ const Drawer = React.forwardRef(function Drawer2(props, ref) {
132
192
  });
133
193
  export {
134
194
  Dialog,
135
- Drawer
195
+ Drawer,
196
+ useDialogClose
136
197
  };