@dreamtree-org/twreact-ui 1.3.7 → 1.3.9
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/DESIGN.md +11 -3
- package/README.md +7 -1
- package/ai-skills/dreamtree-ui.md +2 -2
- package/dist/charts/index.js +69 -24
- package/dist/components/charts/chartTheme.d.ts +2 -5
- package/dist/components/charts/chartTheme.d.ts.map +1 -1
- package/dist/components/core/Table/Table.d.ts.map +1 -1
- package/dist/components/core/Table/useClickOutside.d.ts +5 -2
- package/dist/components/core/Table/useClickOutside.d.ts.map +1 -1
- package/dist/components/utility/Portal.d.ts +6 -0
- package/dist/components/utility/Portal.d.ts.map +1 -0
- package/dist/esm/components/charts/CartesianChart.js +2 -1
- package/dist/esm/components/charts/DonutChart.js +2 -1
- package/dist/esm/components/charts/Sparkline.js +2 -1
- package/dist/esm/components/charts/chartTheme.js +1 -22
- package/dist/esm/components/core/Button.js +8 -8
- package/dist/esm/components/core/Table/Table.js +11 -5
- package/dist/esm/hooks/useClickOutside.js +51 -0
- package/dist/esm/hooks/useMediaQuery.js +61 -0
- package/dist/esm/hooks/useReducedMotion.js +17 -0
- package/dist/esm/hooks/useScrollLock.js +76 -9
- package/dist/esm/hooks/useTheme.js +6 -1
- package/dist/esm/utils/position.js +6 -0
- package/dist/hooks/useClickOutside.d.ts +14 -0
- package/dist/hooks/useClickOutside.d.ts.map +1 -0
- package/dist/hooks/useFloatingPosition.d.ts +37 -0
- package/dist/hooks/useFloatingPosition.d.ts.map +1 -0
- package/dist/hooks/useMediaQuery.d.ts +22 -0
- package/dist/hooks/useMediaQuery.d.ts.map +1 -0
- package/dist/hooks/usePresence.d.ts +16 -0
- package/dist/hooks/usePresence.d.ts.map +1 -0
- package/dist/hooks/usePresentation.d.ts +9 -0
- package/dist/hooks/usePresentation.d.ts.map +1 -0
- package/dist/hooks/useReducedMotion.d.ts +10 -0
- package/dist/hooks/useReducedMotion.d.ts.map +1 -0
- package/dist/hooks/useScrollLock.d.ts +11 -1
- package/dist/hooks/useScrollLock.d.ts.map +1 -1
- package/dist/hooks/useTheme.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.esm.css +1 -1
- package/dist/index.esm.js +142 -34
- package/dist/index.js +142 -34
- package/dist/tokens/tailwind3-preset.cjs +101 -8
- package/dist/tokens/theme.css +27 -0
- package/dist/tokens/tokens.json +175 -0
- package/dist/tokens/vars.css +2 -0
- package/dist/utils/position.d.ts.map +1 -1
- package/dist/utils/touchTarget.d.ts +1 -0
- package/dist/utils/touchTarget.d.ts.map +1 -1
- package/mcp/catalog.snapshot.json +1 -1
- package/package.json +4 -15
- package/tokens/lib.cjs +35 -0
- package/tokens/tokens.json +58 -2
- package/dist/esm/components/core/Table/useClickOutside.js +0 -26
package/DESIGN.md
CHANGED
|
@@ -37,7 +37,11 @@ typography:
|
|
|
37
37
|
rounded:
|
|
38
38
|
control: 8px
|
|
39
39
|
card: 12px
|
|
40
|
+
sheet: 16px
|
|
40
41
|
pill: 9999px
|
|
42
|
+
elevation: [xs, sm, md, lg, xl, overlay]
|
|
43
|
+
zIndex: { base: 0, sticky: 20, dropdown: 30, overlay: 40, modal: 50, popover: 60, toast: 70, tooltip: 80 }
|
|
44
|
+
motion: { fast: 120ms, base: 200ms, slow: 320ms }
|
|
41
45
|
spacing:
|
|
42
46
|
grid: 4px
|
|
43
47
|
steps: [4, 8, 12, 16, 20, 24, 32]
|
|
@@ -81,9 +85,12 @@ Every Dreamtree product screen, and every prototype generated by Dreamtree Studi
|
|
|
81
85
|
## 3. Type, space and shape
|
|
82
86
|
|
|
83
87
|
- Font: Inter. Numbers in tables and KPIs use `tabular-nums`.
|
|
84
|
-
- Scale: page title `text-
|
|
88
|
+
- Scale: use the role classes `text-page-title`, `text-section-title` (both semibold), `text-body`, `text-caption text-muted` and `text-kpi` (semibold). The older `text-2xl font-semibold` / `text-base` / `text-sm` / `text-xs` / `text-3xl` spellings are equivalent. For tiny labels use `text-micro`, never `text-[10px]` or `text-[11px]`. Code and ids use `font-mono`.
|
|
89
|
+
- Elevation: `shadow-xs` … `shadow-xl`, and `shadow-overlay` for dialogs, sheets, popovers and menus. Shadows deepen automatically in dark mode.
|
|
90
|
+
- Layers: `z-sticky`, `z-dropdown`, `z-overlay`, `z-modal`, `z-popover`, `z-toast`, `z-tooltip`. Never `z-50` or an inline `zIndex`.
|
|
91
|
+
- Motion: `duration-fast` (120 ms), `duration-base` (200 ms), `duration-slow` (320 ms) with `ease-standard`, `ease-emphasized` or `ease-spring-out`. Keyframes: `animate-fade-in` / `animate-fade-out`, `animate-scale-in` / `animate-scale-out`, `animate-slide-up`, `animate-slide-down` / `animate-slide-down-out`, `animate-sheet-in` / `animate-sheet-out`. Always pair one with `motion-reduce:animate-none` (e.g. `animate-sheet-in motion-reduce:animate-none`).
|
|
85
92
|
- **No arbitrary values** (`w-[312px]`, `text-[13px]`, `p-[7px]`). Use the Tailwind scale on a 4 px grid: steps 1, 2, 3, 4, 5, 6, 8.
|
|
86
|
-
- Radius: `rounded-control` (8 px) for buttons, inputs and menus; `rounded-card` (12 px) for cards and dialogs; `rounded-full` for pills and badges.
|
|
93
|
+
- Radius: `rounded-control` (8 px) for buttons, inputs and menus; `rounded-card` (12 px) for cards and dialogs; `rounded-sheet` (16 px) for bottom-sheet top corners; `rounded-pill` (or `rounded-full`) for pills and badges.
|
|
87
94
|
- Sentence case everywhere ("Add obligation", not "Add Obligation").
|
|
88
95
|
|
|
89
96
|
## 4. Page anatomy
|
|
@@ -107,9 +114,10 @@ Every Dreamtree product screen, and every prototype generated by Dreamtree Studi
|
|
|
107
114
|
## 7. Responsive and accessible
|
|
108
115
|
|
|
109
116
|
- No horizontal page scroll at **390 px**. Below `lg` the shell becomes a top bar with a drawer. Wide tables scroll inside their card.
|
|
110
|
-
- Tap targets are at least
|
|
117
|
+
- Tap targets are at least **44 px** on touch screens (Apple HIG; WCAG 2.5.5). Icon-only controls grow to 44×44 on `pointer: coarse` and keep their compact size with a mouse. Pressables shrink slightly while pressed (`active:scale-[0.97]`, off under reduced motion). Every control has a visible label. Focus is visible for keyboard users (`focus-visible:ring-2 focus-visible:ring-primary-500 focus-visible:ring-offset-2`); a mouse click doesn't leave a ring.
|
|
111
118
|
- Text contrast is at least 4.5:1. Status is never shown by colour alone: badges carry words.
|
|
112
119
|
- Respect `prefers-reduced-motion`.
|
|
120
|
+
- Phones: pages use `viewport-fit=cover`; bars that touch a screen edge pad with `pt-safe` / `pb-safe` / `px-safe` (or `inset-safe`). Full-height layouts use `min-h-dvh` / `h-dvh`, never `100vh`. Text fields render at 16 px on touch screens so iOS doesn't zoom on focus.
|
|
113
121
|
|
|
114
122
|
## 8. Data and content
|
|
115
123
|
|
package/README.md
CHANGED
|
@@ -881,7 +881,7 @@ module.exports = {
|
|
|
881
881
|
@source "../node_modules/@dreamtree-org/twreact-ui/dist/index.esm.js";
|
|
882
882
|
```
|
|
883
883
|
|
|
884
|
-
The palettes are `primary`, `secondary`, `success`, `warning`, `error`, `info` and `gray` (steps 50–950), so `bg-info-50` / `text-info-600` work out of the box; `info` is for informational and scheduled states that are not the brand accent. This adds semantic utilities next to the palettes: `bg-surface`, `bg-background`, `text-foreground`, `text-muted`, `border-line`, `bg-shell`, `text-micro`, `rounded-control` and `rounded-card`. Add a brand by creating `tokens/brands/<name>.json`; it overrides only the steps and roles you list.
|
|
884
|
+
The palettes are `primary`, `secondary`, `success`, `warning`, `error`, `info` and `gray` (steps 50–950), so `bg-info-50` / `text-info-600` work out of the box; `info` is for informational and scheduled states that are not the brand accent. This adds semantic utilities next to the palettes: `bg-surface`, `bg-background`, `text-foreground`, `text-muted`, `border-line`, `bg-shell`, `text-micro`, `rounded-control` and `rounded-card`. Non-colour scales come from the same file: type roles (`text-page-title`, `text-section-title`, `text-body`, `text-caption`, `text-kpi`), `rounded-sheet` / `rounded-pill`, `font-mono`, elevation (`shadow-xs` … `shadow-xl`, `shadow-overlay`; deeper in dark mode via `--dt-shadow-strength`), z-layers (`z-sticky`, `z-dropdown`, `z-overlay`, `z-modal`, `z-popover`, `z-toast`, `z-tooltip`) and motion (`duration-fast|base|slow`, `ease-standard|emphasized|spring-out`). The Tailwind 3 preset and Tailwind 4 theme include them. Add a brand by creating `tokens/brands/<name>.json`; it overrides only the steps and roles you list.
|
|
885
885
|
|
|
886
886
|
**Design rules:** [`DESIGN.md`](DESIGN.md) is the rule set for product screens and AI agents: tokens only, blocks first, loading, empty and error states, 390 px, no native dialogs. It is generated from the tokens (`npm run build:tokens`).
|
|
887
887
|
|
|
@@ -889,6 +889,10 @@ The palettes are `primary`, `secondary`, `success`, `warning`, `error`, `info` a
|
|
|
889
889
|
|
|
890
890
|
**SSR:** `Tabs`, `Tooltip` and `Switch` generate ids with `React.useId()`, so server-rendered pages (Next.js) hydrate without mismatches. The `/charts` components do the same for their SVG ids, and they render token colours and reduced-motion state the way the server did until hydration finishes.
|
|
891
891
|
|
|
892
|
+
## Mobile and touch
|
|
893
|
+
|
|
894
|
+
The stylesheet ships a phone baseline: no tap highlight, no iOS zoom when a field is focused (text fields are 16px on touch screens; add `dt-keep-font-size` to opt one out), `color-scheme` that follows the theme, and no rubber-band overscroll on `body` (add `dt-overscroll-auto` to `<html>` to keep pull-to-refresh). Pad edge bars with `pt-safe` / `pb-safe` / `px-safe` / `inset-safe` — they need `<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">` — and size full-height layouts with `min-h-dvh` instead of `100vh`. Touch targets grow to 44px on coarse pointers.
|
|
895
|
+
|
|
892
896
|
## Dark Mode
|
|
893
897
|
|
|
894
898
|
Enable dark mode with the built-in theme provider:
|
|
@@ -905,6 +909,8 @@ function App() {
|
|
|
905
909
|
}
|
|
906
910
|
```
|
|
907
911
|
|
|
912
|
+
`ThemeProvider` sets `data-theme`, the `.dark` class and `color-scheme` on `<html>`, so `dark:` utilities work whether your `tailwind.config.js` uses `darkMode: 'class'` or a `[data-theme="dark"]` selector.
|
|
913
|
+
|
|
908
914
|
Surfaces such as `Card`, `Sidebar`, `Navbar`, `Table`, `FootNav` and `Dropdown` use the semantic `bg-surface` / `bg-background` / `border-line` / `text-foreground` / `text-muted` tokens, which switch under `.dark` (or `[data-theme="dark"]`). Re-theme them by overriding the `--dt-surface`, `--dt-background`, `--dt-line`, … variables.
|
|
909
915
|
|
|
910
916
|
## Examples
|
|
@@ -131,7 +131,7 @@ The library exports four kinds of things from `@dreamtree-org/twreact-ui`:
|
|
|
131
131
|
| Hook | Purpose |
|
|
132
132
|
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
133
133
|
| `useTheme` | Read/write the current theme (`light | dark`); pairs with `<ThemeProvider>`. Throws if used outside the provider. |
|
|
134
|
-
| `ThemeProvider` | Provider that persists theme to `localStorage` (`dreamtree-theme`) and toggles `data-theme` on `<html>`. |
|
|
134
|
+
| `ThemeProvider` | Provider that persists theme to `localStorage` (`dreamtree-theme`) and toggles `data-theme` and the `.dark` class on `<html>`. |
|
|
135
135
|
| `useApi` | Thin axios wrapper. Returns `{data, error, loading, lastRequest, fetchData, fetchApi, fetchApiWith}`. Accepts `BASE_URL`, `DEFAULT_HEADERS`, `apiMap`. |
|
|
136
136
|
| `useMixins` | Bridge between component state, Redux store, and an in-memory cache. Power-user hook — prefer local React state for new code. |
|
|
137
137
|
|
|
@@ -320,7 +320,7 @@ function ThemeToggle() {
|
|
|
320
320
|
|
|
321
321
|
`useTheme` MUST be called inside `<ThemeProvider>`; otherwise it throws.
|
|
322
322
|
`<ThemeProvider>` persists to `localStorage` (`dreamtree-theme`) and
|
|
323
|
-
toggles `data-theme` on `<html>`.
|
|
323
|
+
toggles `data-theme` and the `.dark` class on `<html>`.
|
|
324
324
|
|
|
325
325
|
### useApi
|
|
326
326
|
|
package/dist/charts/index.js
CHANGED
|
@@ -174,10 +174,75 @@ var ChartFrame = /*#__PURE__*/React__default.default.forwardRef(function ChartFr
|
|
|
174
174
|
}));
|
|
175
175
|
});
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
function
|
|
179
|
-
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return
|
|
180
|
-
return window.matchMedia(
|
|
177
|
+
// Internal (not exported). SSR-safe media-query subscription.
|
|
178
|
+
function getMediaQueryList(query) {
|
|
179
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return null;
|
|
180
|
+
return window.matchMedia(query) || null;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Subscribes `onChange` to a MediaQueryList's change events. Uses
|
|
184
|
+
* `addEventListener` where available and falls back to the deprecated
|
|
185
|
+
* `addListener` (Safari < 14). Returns the unsubscribe function.
|
|
186
|
+
*/
|
|
187
|
+
function subscribeMediaQuery(query, onChange) {
|
|
188
|
+
var mql = getMediaQueryList(query);
|
|
189
|
+
if (!mql) return function () {};
|
|
190
|
+
if (typeof mql.addEventListener === "function") {
|
|
191
|
+
mql.addEventListener("change", onChange);
|
|
192
|
+
return function () {
|
|
193
|
+
var _mql$removeEventListe;
|
|
194
|
+
return (_mql$removeEventListe = mql.removeEventListener) === null || _mql$removeEventListe === void 0 ? void 0 : _mql$removeEventListe.call(mql, "change", onChange);
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
if (typeof mql.addListener === "function") {
|
|
198
|
+
mql.addListener(onChange);
|
|
199
|
+
return function () {
|
|
200
|
+
var _mql$removeListener;
|
|
201
|
+
return (_mql$removeListener = mql.removeListener) === null || _mql$removeListener === void 0 ? void 0 : _mql$removeListener.call(mql, onChange);
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
return function () {};
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* `true` while `query` matches; follows changes live.
|
|
208
|
+
*
|
|
209
|
+
* On the server (and while hydrating) it returns `defaultValue`, so server and
|
|
210
|
+
* client markup agree; the real value is applied right after hydration. Without
|
|
211
|
+
* `window.matchMedia` it always returns `defaultValue`.
|
|
212
|
+
*
|
|
213
|
+
* @param {string} query - a CSS media query, e.g. "(max-width: 639px)".
|
|
214
|
+
* @param {{ defaultValue?: boolean }} [options]
|
|
215
|
+
* @returns {boolean}
|
|
216
|
+
*/
|
|
217
|
+
function useMediaQuery(query) {
|
|
218
|
+
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
|
|
219
|
+
_ref$defaultValue = _ref.defaultValue,
|
|
220
|
+
defaultValue = _ref$defaultValue === void 0 ? false : _ref$defaultValue;
|
|
221
|
+
var subscribe = React.useCallback(function (onChange) {
|
|
222
|
+
return subscribeMediaQuery(query, onChange);
|
|
223
|
+
}, [query]);
|
|
224
|
+
var getSnapshot = function getSnapshot() {
|
|
225
|
+
var mql = getMediaQueryList(query);
|
|
226
|
+
return mql ? Boolean(mql.matches) : defaultValue;
|
|
227
|
+
};
|
|
228
|
+
var getServerSnapshot = function getServerSnapshot() {
|
|
229
|
+
return defaultValue;
|
|
230
|
+
};
|
|
231
|
+
return React.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Internal (not exported). Shared by the charts and the overlay primitives.
|
|
235
|
+
var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
|
|
236
|
+
/**
|
|
237
|
+
* `true` while the user asks the OS for reduced motion; follows changes live.
|
|
238
|
+
* `false` on the server and while hydrating, so the first render matches.
|
|
239
|
+
*
|
|
240
|
+
* @returns {boolean}
|
|
241
|
+
*/
|
|
242
|
+
function useReducedMotion() {
|
|
243
|
+
return useMediaQuery(REDUCED_MOTION_QUERY, {
|
|
244
|
+
defaultValue: false
|
|
245
|
+
});
|
|
181
246
|
}
|
|
182
247
|
|
|
183
248
|
var CHART_PALETTES = ["primary", "secondary", "success", "warning", "error", "info", "gray"];
|
|
@@ -267,26 +332,6 @@ function useChartTheme(categories, colors) {
|
|
|
267
332
|
series: seriesColors(categories, colors, live)
|
|
268
333
|
};
|
|
269
334
|
}
|
|
270
|
-
function subscribeReducedMotion(onChange) {
|
|
271
|
-
var _query$addEventListen;
|
|
272
|
-
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return function () {};
|
|
273
|
-
var query = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
274
|
-
(_query$addEventListen = query.addEventListener) === null || _query$addEventListen === void 0 || _query$addEventListen.call(query, "change", onChange);
|
|
275
|
-
return function () {
|
|
276
|
-
var _query$removeEventLis;
|
|
277
|
-
return (_query$removeEventLis = query.removeEventListener) === null || _query$removeEventLis === void 0 ? void 0 : _query$removeEventLis.call(query, "change", onChange);
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
var serverReducedMotion = function serverReducedMotion() {
|
|
281
|
-
return false;
|
|
282
|
-
};
|
|
283
|
-
/**
|
|
284
|
-
* `true` while the user asks the OS for reduced motion; follows changes live.
|
|
285
|
-
* `false` on the server and while hydrating, so the first render matches.
|
|
286
|
-
*/
|
|
287
|
-
function useReducedMotion() {
|
|
288
|
-
return React.useSyncExternalStore(subscribeReducedMotion, prefersReducedMotion, serverReducedMotion);
|
|
289
|
-
}
|
|
290
335
|
var defaultValueFormatter = function defaultValueFormatter(value) {
|
|
291
336
|
return typeof value === "number" ? value.toLocaleString() : value == null ? "" : String(value);
|
|
292
337
|
};
|
|
@@ -18,11 +18,6 @@ export function useChartTheme(categories: any, colors: any): {
|
|
|
18
18
|
};
|
|
19
19
|
series: any;
|
|
20
20
|
};
|
|
21
|
-
/**
|
|
22
|
-
* `true` while the user asks the OS for reduced motion; follows changes live.
|
|
23
|
-
* `false` on the server and while hydrating, so the first render matches.
|
|
24
|
-
*/
|
|
25
|
-
export function useReducedMotion(): boolean;
|
|
26
21
|
/**
|
|
27
22
|
* A per-instance id for the SVG defs recharts draws (clip paths). recharts 2
|
|
28
23
|
* falls back to a module-level counter, which differs between the server and the
|
|
@@ -30,7 +25,9 @@ export function useReducedMotion(): boolean;
|
|
|
30
25
|
* is safe inside `url(#…)`.
|
|
31
26
|
*/
|
|
32
27
|
export function useChartId(): string;
|
|
28
|
+
export { useReducedMotion };
|
|
33
29
|
export const CHART_PALETTES: string[];
|
|
34
30
|
export const DEFAULT_CHART_COLORS: string[];
|
|
35
31
|
export function defaultValueFormatter(value: any): string;
|
|
32
|
+
import { useReducedMotion } from "../../hooks/useReducedMotion";
|
|
36
33
|
//# sourceMappingURL=chartTheme.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chartTheme.d.ts","sourceRoot":"","sources":["../../../src/components/charts/chartTheme.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"chartTheme.d.ts","sourceRoot":"","sources":["../../../src/components/charts/chartTheme.js"],"names":[],"mappings":"AAmBA,sGAAsG;AACtG,oDAGC;AAgBD;;;;GAIG;AACH,8DAGC;AAED,gFAGC;AAwBD;;;;GAIG;AACH;;;;;EAIC;AAKD;;;;;GAKG;AACH,qCAEC;;AA5FD,sCAAsG;AAEtG,4CAA4G;AA+ErG,0DACkF;iCAvFxD,8BAA8B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Table.d.ts","sourceRoot":"","sources":["../../../../src/components/core/Table/Table.jsx"],"names":[],"mappings":";AA8CA,+
|
|
1
|
+
{"version":3,"file":"Table.d.ts","sourceRoot":"","sources":["../../../../src/components/core/Table/Table.jsx"],"names":[],"mappings":";AA8CA,+EA8gDG;kBAnjDI,OAAO"}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Legacy shim — the implementation lives in `src/hooks/useClickOutside.js`.
|
|
3
|
+
* Kept at this path (and with the old positional signature) because
|
|
4
|
+
* `core/Table/index.js` re-exports it.
|
|
5
|
+
*
|
|
3
6
|
* @param {Array} refs - Array of React refs to check
|
|
4
7
|
* @param {boolean} isActive - Whether the hook should be active
|
|
5
|
-
* @param {Function} onClickOutside - Callback when
|
|
8
|
+
* @param {Function} onClickOutside - Callback when a press lands outside
|
|
6
9
|
*/
|
|
7
10
|
export function useClickOutside(refs: any[], isActive: boolean, onClickOutside: Function): void;
|
|
8
11
|
export default useClickOutside;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useClickOutside.d.ts","sourceRoot":"","sources":["../../../../src/components/core/Table/useClickOutside.js"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"useClickOutside.d.ts","sourceRoot":"","sources":["../../../../src/components/core/Table/useClickOutside.js"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,uDAHW,OAAO,kCAKjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Portal.d.ts","sourceRoot":"","sources":["../../../src/components/utility/Portal.jsx"],"names":[],"mappings":";AAcA;;;uCAOC"}
|
|
@@ -4,7 +4,8 @@ import { jsx, jsxs } from 'react/jsx-runtime';
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
import { ResponsiveContainer, AreaChart, BarChart, LineChart, CartesianGrid, XAxis, YAxis, Tooltip, Bar, Area, Line } from 'recharts';
|
|
6
6
|
import { ChartFrame, ChartTooltipContent } from './ChartFrame.js';
|
|
7
|
-
import { useChartTheme,
|
|
7
|
+
import { useChartTheme, useChartId, defaultValueFormatter } from './chartTheme.js';
|
|
8
|
+
import { useReducedMotion } from '../../hooks/useReducedMotion.js';
|
|
8
9
|
|
|
9
10
|
var _excluded = ["kind", "data", "index", "categories", "colors", "valueFormatter", "xAxisFormatter", "height", "showLegend", "showTooltip", "showXAxis", "showYAxis", "showGrid", "yAxisWidth", "curveType", "stacked", "horizontal", "showDots", "connectNulls", "animate", "title", "description", "emptyState", "accessibleTable", "className"];
|
|
10
11
|
var ROOTS = {
|
|
@@ -4,7 +4,8 @@ import { jsx, jsxs } from 'react/jsx-runtime';
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
import { ResponsiveContainer, PieChart, Tooltip, Pie } from 'recharts';
|
|
6
6
|
import { ChartFrame, ChartTooltipContent } from './ChartFrame.js';
|
|
7
|
-
import { useChartTheme,
|
|
7
|
+
import { useChartTheme, useChartId, defaultValueFormatter } from './chartTheme.js';
|
|
8
|
+
import { useReducedMotion } from '../../hooks/useReducedMotion.js';
|
|
8
9
|
|
|
9
10
|
var _excluded = ["data", "index", "category", "colors", "valueFormatter", "variant", "innerRadius", "outerRadius", "centerLabel", "height", "showLegend", "showTooltip", "animate", "title", "description", "emptyState", "accessibleTable", "className"];
|
|
10
11
|
var DonutChart = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
|
|
@@ -4,7 +4,8 @@ import { jsx, jsxs } from 'react/jsx-runtime';
|
|
|
4
4
|
import React from 'react';
|
|
5
5
|
import { ResponsiveContainer, AreaChart, LineChart, YAxis, Tooltip, Area, Line } from 'recharts';
|
|
6
6
|
import { ChartFrame, ChartTooltipContent } from './ChartFrame.js';
|
|
7
|
-
import { useChartTheme,
|
|
7
|
+
import { useChartTheme, useChartId, defaultValueFormatter } from './chartTheme.js';
|
|
8
|
+
import { useReducedMotion } from '../../hooks/useReducedMotion.js';
|
|
8
9
|
|
|
9
10
|
var _excluded = ["data", "category", "index", "color", "type", "valueFormatter", "height", "showTooltip", "animate", "title", "description", "emptyState", "accessibleTable", "className"];
|
|
10
11
|
/** A word-sized trend line: no axes, grid or legend — just the shape of the data. */
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { slicedToArray as _slicedToArray } from '../../_virtual/_rollupPluginBabelHelpers.js';
|
|
3
3
|
import { useSyncExternalStore, useId } from 'react';
|
|
4
|
-
import { prefersReducedMotion } from '../../utils/position.js';
|
|
5
4
|
|
|
6
5
|
var CHART_PALETTES = ["primary", "secondary", "success", "warning", "error", "info", "gray"];
|
|
7
6
|
var DEFAULT_CHART_COLORS = ["primary", "success", "warning", "error", "info", "secondary", "gray"];
|
|
@@ -90,26 +89,6 @@ function useChartTheme(categories, colors) {
|
|
|
90
89
|
series: seriesColors(categories, colors, live)
|
|
91
90
|
};
|
|
92
91
|
}
|
|
93
|
-
function subscribeReducedMotion(onChange) {
|
|
94
|
-
var _query$addEventListen;
|
|
95
|
-
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return function () {};
|
|
96
|
-
var query = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
97
|
-
(_query$addEventListen = query.addEventListener) === null || _query$addEventListen === void 0 || _query$addEventListen.call(query, "change", onChange);
|
|
98
|
-
return function () {
|
|
99
|
-
var _query$removeEventLis;
|
|
100
|
-
return (_query$removeEventLis = query.removeEventListener) === null || _query$removeEventLis === void 0 ? void 0 : _query$removeEventLis.call(query, "change", onChange);
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
var serverReducedMotion = function serverReducedMotion() {
|
|
104
|
-
return false;
|
|
105
|
-
};
|
|
106
|
-
/**
|
|
107
|
-
* `true` while the user asks the OS for reduced motion; follows changes live.
|
|
108
|
-
* `false` on the server and while hydrating, so the first render matches.
|
|
109
|
-
*/
|
|
110
|
-
function useReducedMotion() {
|
|
111
|
-
return useSyncExternalStore(subscribeReducedMotion, prefersReducedMotion, serverReducedMotion);
|
|
112
|
-
}
|
|
113
92
|
var defaultValueFormatter = function defaultValueFormatter(value) {
|
|
114
93
|
return typeof value === "number" ? value.toLocaleString() : value == null ? "" : String(value);
|
|
115
94
|
};
|
|
@@ -123,4 +102,4 @@ function useChartId() {
|
|
|
123
102
|
return "dt-chart-".concat(useId().replace(/[^A-Za-z0-9_-]/g, ""));
|
|
124
103
|
}
|
|
125
104
|
|
|
126
|
-
export { CHART_PALETTES, DEFAULT_CHART_COLORS, defaultValueFormatter, resolveColor, seriesColors, tokenVar, useChartId, useChartTheme
|
|
105
|
+
export { CHART_PALETTES, DEFAULT_CHART_COLORS, defaultValueFormatter, resolveColor, seriesColors, tokenVar, useChartId, useChartTheme };
|
|
@@ -26,15 +26,15 @@ var Button = /*#__PURE__*/forwardRef(function (_ref, ref) {
|
|
|
26
26
|
var Component = as || (props.href !== undefined ? 'a' : 'button');
|
|
27
27
|
var isNativeButton = Component === 'button';
|
|
28
28
|
var isDisabled = disabled || loading;
|
|
29
|
-
var baseClasses = cn('inline-flex items-center justify-center rounded-md font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none aria-disabled:opacity-50 aria-disabled:pointer-events-none', {
|
|
29
|
+
var baseClasses = cn('inline-flex items-center justify-center rounded-md font-medium transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none aria-disabled:opacity-50 aria-disabled:pointer-events-none', {
|
|
30
30
|
// Variants
|
|
31
|
-
'bg-primary-600 text-white hover:bg-primary-700 focus:ring-primary-500': variant === 'primary',
|
|
32
|
-
'bg-secondary-100 text-secondary-900 hover:bg-secondary-200 focus:ring-secondary-500': variant === 'secondary',
|
|
33
|
-
'bg-transparent border border-gray-300 hover:bg-gray-50 focus:ring-primary-500 dark:border-gray-600 dark:text-gray-100 dark:hover:bg-gray-800 dark:focus:ring-offset-gray-900': variant === 'outline',
|
|
34
|
-
'bg-transparent text-primary-600 hover:bg-primary-50 focus:ring-primary-500': variant === 'ghost',
|
|
35
|
-
'bg-error-600 text-white hover:bg-error-700 focus:ring-error-500': variant === 'destructive',
|
|
36
|
-
'bg-success-600 text-white hover:bg-success-700 focus:ring-success-500': variant === 'success',
|
|
37
|
-
'bg-warning-600 text-white hover:bg-warning-700 focus:ring-warning-500': variant === 'warning',
|
|
31
|
+
'bg-primary-600 text-white hover:bg-primary-700 focus-visible:ring-primary-500': variant === 'primary',
|
|
32
|
+
'bg-secondary-100 text-secondary-900 hover:bg-secondary-200 focus-visible:ring-secondary-500': variant === 'secondary',
|
|
33
|
+
'bg-transparent border border-gray-300 hover:bg-gray-50 focus-visible:ring-primary-500 dark:border-gray-600 dark:text-gray-100 dark:hover:bg-gray-800 dark:focus-visible:ring-offset-gray-900': variant === 'outline',
|
|
34
|
+
'bg-transparent text-primary-600 hover:bg-primary-50 focus-visible:ring-primary-500': variant === 'ghost',
|
|
35
|
+
'bg-error-600 text-white hover:bg-error-700 focus-visible:ring-error-500': variant === 'destructive',
|
|
36
|
+
'bg-success-600 text-white hover:bg-success-700 focus-visible:ring-success-500': variant === 'success',
|
|
37
|
+
'bg-warning-600 text-white hover:bg-warning-700 focus-visible:ring-warning-500': variant === 'warning',
|
|
38
38
|
// Sizes — every value of the shared `Size` type gets a real height.
|
|
39
39
|
'h-7 px-2 text-xs': size === 'xs',
|
|
40
40
|
'h-8 px-3 text-xs': size === 'sm',
|
|
@@ -8,7 +8,7 @@ import { Plus, RefreshCcw, Save, X, AlertCircle, Search, ChevronUp, ChevronDown,
|
|
|
8
8
|
import Select from '../Select.js';
|
|
9
9
|
import { DEFAULT_ACTIONS } from './constants.js';
|
|
10
10
|
import { getRowKey, mergeDefaultActions } from './helpers.js';
|
|
11
|
-
import { useClickOutside } from '
|
|
11
|
+
import { useClickOutside } from '../../../hooks/useClickOutside.js';
|
|
12
12
|
import { PaginationHeader, PaginationFooter } from './Pagination.js';
|
|
13
13
|
import ColumnMenu from './ColumnMenu.js';
|
|
14
14
|
import MobileFilters from './MobileFilters.js';
|
|
@@ -524,8 +524,10 @@ var Table = /*#__PURE__*/forwardRef(function Table(_ref, ref) {
|
|
|
524
524
|
});
|
|
525
525
|
}
|
|
526
526
|
}, []);
|
|
527
|
-
useClickOutside([tableContainerRef],
|
|
528
|
-
setEditingCell(null);
|
|
527
|
+
useClickOutside([tableContainerRef], function () {
|
|
528
|
+
return setEditingCell(null);
|
|
529
|
+
}, {
|
|
530
|
+
enabled: interactive && Boolean(editingCell)
|
|
529
531
|
});
|
|
530
532
|
var actionButtonRefs = useRef({});
|
|
531
533
|
// Row components register their kebab buttons through this callback rather
|
|
@@ -603,8 +605,12 @@ var Table = /*#__PURE__*/forwardRef(function Table(_ref, ref) {
|
|
|
603
605
|
var closeActionMenu = useCallback(function () {
|
|
604
606
|
return setActiveActionRowKey(null);
|
|
605
607
|
}, []);
|
|
606
|
-
useClickOutside([columnMenuRef, columnToggleBtnRef],
|
|
607
|
-
|
|
608
|
+
useClickOutside([columnMenuRef, columnToggleBtnRef], closeColumnMenu, {
|
|
609
|
+
enabled: showColumnMenu
|
|
610
|
+
});
|
|
611
|
+
useClickOutside([actionMenuRef], closeActionMenu, {
|
|
612
|
+
enabled: activeActionRowKey !== null
|
|
613
|
+
});
|
|
608
614
|
// Computed values
|
|
609
615
|
var activeAnchorEl = useMemo(function () {
|
|
610
616
|
if (activeActionRowKey === null) return null;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useEffect } from 'react';
|
|
3
|
+
import { useLatestRef } from './useLatestRef.js';
|
|
4
|
+
|
|
5
|
+
// Internal (not exported). Calls `onClickOutside` when a press lands outside
|
|
6
|
+
// every element in `refs`.
|
|
7
|
+
/** Events to listen to: `pointerdown` where supported, else mouse + touch. */
|
|
8
|
+
function outsidePressEvents() {
|
|
9
|
+
if (typeof window !== "undefined" && typeof window.PointerEvent === "function") {
|
|
10
|
+
return ["pointerdown"];
|
|
11
|
+
}
|
|
12
|
+
return ["mousedown", "touchstart"];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* @param {React.RefObject|React.RefObject[]} refs - one ref or an array of
|
|
16
|
+
* refs; a press inside any of them is "inside". Refs with no node are ignored.
|
|
17
|
+
* @param {(event: Event) => void} onClickOutside - called with the event.
|
|
18
|
+
* @param {{ enabled?: boolean }} [options] - `enabled` (default true) attaches
|
|
19
|
+
* or detaches the listeners.
|
|
20
|
+
*/
|
|
21
|
+
function useClickOutside(refs, onClickOutside) {
|
|
22
|
+
var _ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
|
|
23
|
+
_ref$enabled = _ref.enabled,
|
|
24
|
+
enabled = _ref$enabled === void 0 ? true : _ref$enabled;
|
|
25
|
+
var refsRef = useLatestRef(refs);
|
|
26
|
+
var handlerRef = useLatestRef(onClickOutside);
|
|
27
|
+
useEffect(function () {
|
|
28
|
+
if (!enabled || typeof document === "undefined") return undefined;
|
|
29
|
+
var handler = function handler(event) {
|
|
30
|
+
var _handlerRef$current;
|
|
31
|
+
var current = refsRef.current;
|
|
32
|
+
var list = Array.isArray(current) ? current : [current];
|
|
33
|
+
var inside = list.some(function (ref) {
|
|
34
|
+
var node = ref === null || ref === void 0 ? void 0 : ref.current;
|
|
35
|
+
return Boolean(node && typeof node.contains === "function" && node.contains(event.target));
|
|
36
|
+
});
|
|
37
|
+
if (!inside) (_handlerRef$current = handlerRef.current) === null || _handlerRef$current === void 0 || _handlerRef$current.call(handlerRef, event);
|
|
38
|
+
};
|
|
39
|
+
var events = outsidePressEvents();
|
|
40
|
+
events.forEach(function (type) {
|
|
41
|
+
return document.addEventListener(type, handler, true);
|
|
42
|
+
});
|
|
43
|
+
return function () {
|
|
44
|
+
return events.forEach(function (type) {
|
|
45
|
+
return document.removeEventListener(type, handler, true);
|
|
46
|
+
});
|
|
47
|
+
};
|
|
48
|
+
}, [enabled, refsRef, handlerRef]);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { outsidePressEvents, useClickOutside };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback, useSyncExternalStore } from 'react';
|
|
3
|
+
|
|
4
|
+
// Internal (not exported). SSR-safe media-query subscription.
|
|
5
|
+
function getMediaQueryList(query) {
|
|
6
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return null;
|
|
7
|
+
return window.matchMedia(query) || null;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Subscribes `onChange` to a MediaQueryList's change events. Uses
|
|
11
|
+
* `addEventListener` where available and falls back to the deprecated
|
|
12
|
+
* `addListener` (Safari < 14). Returns the unsubscribe function.
|
|
13
|
+
*/
|
|
14
|
+
function subscribeMediaQuery(query, onChange) {
|
|
15
|
+
var mql = getMediaQueryList(query);
|
|
16
|
+
if (!mql) return function () {};
|
|
17
|
+
if (typeof mql.addEventListener === "function") {
|
|
18
|
+
mql.addEventListener("change", onChange);
|
|
19
|
+
return function () {
|
|
20
|
+
var _mql$removeEventListe;
|
|
21
|
+
return (_mql$removeEventListe = mql.removeEventListener) === null || _mql$removeEventListe === void 0 ? void 0 : _mql$removeEventListe.call(mql, "change", onChange);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if (typeof mql.addListener === "function") {
|
|
25
|
+
mql.addListener(onChange);
|
|
26
|
+
return function () {
|
|
27
|
+
var _mql$removeListener;
|
|
28
|
+
return (_mql$removeListener = mql.removeListener) === null || _mql$removeListener === void 0 ? void 0 : _mql$removeListener.call(mql, onChange);
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return function () {};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* `true` while `query` matches; follows changes live.
|
|
35
|
+
*
|
|
36
|
+
* On the server (and while hydrating) it returns `defaultValue`, so server and
|
|
37
|
+
* client markup agree; the real value is applied right after hydration. Without
|
|
38
|
+
* `window.matchMedia` it always returns `defaultValue`.
|
|
39
|
+
*
|
|
40
|
+
* @param {string} query - a CSS media query, e.g. "(max-width: 639px)".
|
|
41
|
+
* @param {{ defaultValue?: boolean }} [options]
|
|
42
|
+
* @returns {boolean}
|
|
43
|
+
*/
|
|
44
|
+
function useMediaQuery(query) {
|
|
45
|
+
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
|
|
46
|
+
_ref$defaultValue = _ref.defaultValue,
|
|
47
|
+
defaultValue = _ref$defaultValue === void 0 ? false : _ref$defaultValue;
|
|
48
|
+
var subscribe = useCallback(function (onChange) {
|
|
49
|
+
return subscribeMediaQuery(query, onChange);
|
|
50
|
+
}, [query]);
|
|
51
|
+
var getSnapshot = function getSnapshot() {
|
|
52
|
+
var mql = getMediaQueryList(query);
|
|
53
|
+
return mql ? Boolean(mql.matches) : defaultValue;
|
|
54
|
+
};
|
|
55
|
+
var getServerSnapshot = function getServerSnapshot() {
|
|
56
|
+
return defaultValue;
|
|
57
|
+
};
|
|
58
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export { subscribeMediaQuery, useMediaQuery };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { useMediaQuery } from './useMediaQuery.js';
|
|
2
|
+
|
|
3
|
+
// Internal (not exported). Shared by the charts and the overlay primitives.
|
|
4
|
+
var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
|
|
5
|
+
/**
|
|
6
|
+
* `true` while the user asks the OS for reduced motion; follows changes live.
|
|
7
|
+
* `false` on the server and while hydrating, so the first render matches.
|
|
8
|
+
*
|
|
9
|
+
* @returns {boolean}
|
|
10
|
+
*/
|
|
11
|
+
function useReducedMotion() {
|
|
12
|
+
return useMediaQuery(REDUCED_MOTION_QUERY, {
|
|
13
|
+
defaultValue: false
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { REDUCED_MOTION_QUERY, useReducedMotion };
|
|
@@ -3,24 +3,91 @@ import { useEffect } from 'react';
|
|
|
3
3
|
|
|
4
4
|
// Internal (not exported). Locks body scroll while any overlay is open. A
|
|
5
5
|
// counter lets two overlays (e.g. a Drawer and a Tour) overlap without the
|
|
6
|
-
// first one to close unlocking the page under the second
|
|
6
|
+
// first one to close unlocking the page under the second: only the outermost
|
|
7
|
+
// lock touches the body, and only the last release restores it.
|
|
8
|
+
//
|
|
9
|
+
// While locked it also
|
|
10
|
+
// - pads the body by the scrollbar width, so the page does not shift when the
|
|
11
|
+
// scrollbar disappears;
|
|
12
|
+
// - on iOS (or with `{ fixedBody: true }`) pins the body with
|
|
13
|
+
// `position: fixed; top: -scrollY; width: 100%`, because iOS Safari ignores
|
|
14
|
+
// `overflow: hidden` on the body, and restores the scroll position after.
|
|
7
15
|
var lock = {
|
|
8
16
|
count: 0,
|
|
9
|
-
|
|
17
|
+
saved: null
|
|
10
18
|
};
|
|
19
|
+
/** iPhone / iPod / iPad, including iPadOS 13+ which reports itself as a Mac. */
|
|
20
|
+
function isIOS() {
|
|
21
|
+
if (typeof navigator === "undefined") return false;
|
|
22
|
+
var platform = navigator.platform || "";
|
|
23
|
+
if (/iP(hone|od|ad)/.test(platform)) return true;
|
|
24
|
+
return platform === "MacIntel" && Number(navigator.maxTouchPoints) > 1;
|
|
25
|
+
}
|
|
26
|
+
function scrollbarWidth() {
|
|
27
|
+
var docWidth = document.documentElement.clientWidth;
|
|
28
|
+
// jsdom (and detached documents) report 0; treat as "no scrollbar".
|
|
29
|
+
if (!docWidth) return 0;
|
|
30
|
+
return Math.max(0, window.innerWidth - docWidth);
|
|
31
|
+
}
|
|
32
|
+
function acquire(fixedBody) {
|
|
33
|
+
var _document = document,
|
|
34
|
+
body = _document.body;
|
|
35
|
+
var style = body.style;
|
|
36
|
+
var saved = {
|
|
37
|
+
overflow: style.overflow,
|
|
38
|
+
paddingRight: style.paddingRight,
|
|
39
|
+
fixed: fixedBody
|
|
40
|
+
};
|
|
41
|
+
var gap = scrollbarWidth();
|
|
42
|
+
if (gap > 0) {
|
|
43
|
+
var current = parseFloat(window.getComputedStyle(body).paddingRight) || 0;
|
|
44
|
+
style.paddingRight = "".concat(current + gap, "px");
|
|
45
|
+
}
|
|
46
|
+
if (fixedBody) {
|
|
47
|
+
saved.position = style.position;
|
|
48
|
+
saved.top = style.top;
|
|
49
|
+
saved.width = style.width;
|
|
50
|
+
saved.scrollX = window.scrollX || window.pageXOffset || 0;
|
|
51
|
+
saved.scrollY = window.scrollY || window.pageYOffset || 0;
|
|
52
|
+
style.position = "fixed";
|
|
53
|
+
style.top = "-".concat(saved.scrollY, "px");
|
|
54
|
+
style.width = "100%";
|
|
55
|
+
}
|
|
56
|
+
style.overflow = "hidden";
|
|
57
|
+
lock.saved = saved;
|
|
58
|
+
}
|
|
59
|
+
function release() {
|
|
60
|
+
var saved = lock.saved;
|
|
61
|
+
lock.saved = null;
|
|
62
|
+
if (!saved) return;
|
|
63
|
+
var style = document.body.style;
|
|
64
|
+
style.overflow = saved.overflow;
|
|
65
|
+
style.paddingRight = saved.paddingRight;
|
|
66
|
+
if (saved.fixed) {
|
|
67
|
+
style.position = saved.position;
|
|
68
|
+
style.top = saved.top;
|
|
69
|
+
style.width = saved.width;
|
|
70
|
+
if (typeof window.scrollTo === "function") window.scrollTo(saved.scrollX, saved.scrollY);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* @param {boolean} active - hold the lock while true.
|
|
75
|
+
* @param {{ fixedBody?: boolean }} [options] - force the iOS-safe
|
|
76
|
+
* `position: fixed` mode; defaults to auto-detecting iOS. Only the outermost
|
|
77
|
+
* lock's mode applies.
|
|
78
|
+
*/
|
|
11
79
|
function useScrollLock(active) {
|
|
80
|
+
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
|
|
81
|
+
fixedBody = _ref.fixedBody;
|
|
12
82
|
useEffect(function () {
|
|
13
83
|
if (!active || typeof document === "undefined") return undefined;
|
|
14
|
-
if (lock.count === 0)
|
|
15
|
-
lock.previous = document.body.style.overflow;
|
|
16
|
-
document.body.style.overflow = "hidden";
|
|
17
|
-
}
|
|
84
|
+
if (lock.count === 0) acquire(fixedBody === undefined ? isIOS() : Boolean(fixedBody));
|
|
18
85
|
lock.count += 1;
|
|
19
86
|
return function () {
|
|
20
87
|
lock.count -= 1;
|
|
21
|
-
if (lock.count === 0)
|
|
88
|
+
if (lock.count === 0) release();
|
|
22
89
|
};
|
|
23
|
-
}, [active]);
|
|
90
|
+
}, [active, fixedBody]);
|
|
24
91
|
}
|
|
25
92
|
|
|
26
|
-
export { useScrollLock };
|
|
93
|
+
export { isIOS, useScrollLock };
|