@boxcustodia/library 2.0.0-alpha.19 → 2.0.0-alpha.20
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/components/button/button.cjs.js +1 -1
- package/dist/components/button/button.es.js +19 -18
- package/dist/components/button/components/base-button.cjs.js +1 -1
- package/dist/components/button/components/base-button.es.js +20 -20
- package/dist/components/calendar/calendar.cjs.js +1 -1
- package/dist/components/calendar/calendar.es.js +1 -0
- package/dist/components/date-picker/date-input.cjs.js +1 -1
- package/dist/components/date-picker/date-input.es.js +92 -75
- package/dist/components/date-picker/date-picker.cjs.js +1 -1
- package/dist/components/date-picker/date-picker.es.js +104 -95
- package/dist/components/date-picker/date-picker.utils.cjs.js +1 -1
- package/dist/components/date-picker/date-picker.utils.es.js +51 -43
- package/dist/components/date-picker/use-hidden-field-value.cjs.js +1 -0
- package/dist/components/date-picker/use-hidden-field-value.es.js +11 -0
- package/dist/components/menu/menu.es.js +1 -9
- package/dist/components/otp/otp.cjs.js +2 -0
- package/dist/components/otp/otp.es.js +93 -0
- package/dist/components/password/password.cjs.js +1 -1
- package/dist/components/password/password.es.js +2 -2
- package/dist/components/select/select.cjs.js +1 -1
- package/dist/components/select/select.es.js +68 -60
- package/dist/hooks/internal/is-apple-device.cjs.js +1 -0
- package/dist/hooks/internal/is-apple-device.es.js +9 -0
- package/dist/hooks/internal/use-latest-ref.cjs.js +1 -0
- package/dist/hooks/internal/use-latest-ref.es.js +11 -0
- package/dist/hooks/use-array/use-array.cjs.js +1 -1
- package/dist/hooks/use-array/use-array.es.js +54 -42
- package/dist/hooks/use-async/use-async.cjs.js +1 -1
- package/dist/hooks/use-async/use-async.es.js +53 -20
- package/dist/hooks/use-boolean/use-boolean.cjs.js +1 -0
- package/dist/hooks/use-boolean/use-boolean.es.js +25 -0
- package/dist/hooks/use-click-outside/use-click-outside.cjs.js +1 -1
- package/dist/hooks/use-click-outside/use-click-outside.es.js +26 -12
- package/dist/hooks/use-debounce-callback/use-debounced-callback.cjs.js +1 -1
- package/dist/hooks/use-debounce-callback/use-debounced-callback.es.js +27 -10
- package/dist/hooks/use-debounce-value/use-debounced-value.cjs.js +1 -1
- package/dist/hooks/use-debounce-value/use-debounced-value.es.js +7 -9
- package/dist/hooks/use-disclosure/use-disclosure.cjs.js +1 -1
- package/dist/hooks/use-disclosure/use-disclosure.es.js +21 -11
- package/dist/hooks/use-document-title/use-document-title.cjs.js +1 -1
- package/dist/hooks/use-document-title/use-document-title.es.js +14 -12
- package/dist/hooks/use-event-listener/use-event-listener.cjs.js +1 -1
- package/dist/hooks/use-event-listener/use-event-listener.es.js +17 -9
- package/dist/hooks/use-hotkey/use-hotkey.cjs.js +1 -1
- package/dist/hooks/use-hotkey/use-hotkey.es.js +30 -14
- package/dist/hooks/use-hotkey/utils/is-input-field.cjs.js +1 -1
- package/dist/hooks/use-hotkey/utils/is-input-field.es.js +4 -2
- package/dist/hooks/use-hotkey/utils/match-and-run.cjs.js +1 -0
- package/dist/hooks/use-hotkey/utils/match-and-run.es.js +12 -0
- package/dist/hooks/use-hotkey/utils/match-key-modifiers.cjs.js +1 -1
- package/dist/hooks/use-hotkey/utils/match-key-modifiers.es.js +13 -12
- package/dist/hooks/use-hover/use-hover.cjs.js +1 -1
- package/dist/hooks/use-hover/use-hover.es.js +32 -17
- package/dist/hooks/use-is-visible/use-is-visible.cjs.js +1 -1
- package/dist/hooks/use-is-visible/use-is-visible.es.js +31 -27
- package/dist/hooks/use-local-storage/use-local-storage.cjs.js +1 -1
- package/dist/hooks/use-local-storage/use-local-storage.es.js +52 -20
- package/dist/hooks/use-media-query/use-media-query.cjs.js +1 -1
- package/dist/hooks/use-media-query/use-media-query.es.js +21 -11
- package/dist/hooks/use-mutation/use-mutation.cjs.js +1 -1
- package/dist/hooks/use-mutation/use-mutation.es.js +36 -22
- package/dist/hooks/use-object/use-object.cjs.js +1 -1
- package/dist/hooks/use-object/use-object.es.js +26 -22
- package/dist/hooks/use-prevent-page-close/use-prevent-page-close.cjs.js +1 -0
- package/dist/hooks/use-prevent-page-close/use-prevent-page-close.es.js +14 -0
- package/dist/hooks/use-step/use-step.cjs.js +1 -1
- package/dist/hooks/use-step/use-step.es.js +25 -24
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +308 -300
- package/dist/src/components/date-picker/date-picker.utils.d.ts +17 -0
- package/dist/src/components/date-picker/use-hidden-field-value.d.ts +12 -0
- package/dist/src/components/index.d.ts +1 -0
- package/dist/src/hooks/index.d.ts +2 -2
- package/dist/src/hooks/internal/index.d.ts +2 -0
- package/dist/src/hooks/internal/is-apple-device.d.ts +12 -0
- package/dist/src/hooks/internal/use-latest-ref.d.ts +12 -0
- package/dist/src/hooks/use-array/use-array.d.ts +24 -11
- package/dist/src/hooks/use-async/use-async.d.ts +16 -13
- package/dist/src/hooks/use-boolean/index.d.ts +1 -0
- package/dist/src/hooks/use-boolean/use-boolean.d.ts +15 -0
- package/dist/src/hooks/use-boolean/use-boolean.test.d.ts +1 -0
- package/dist/src/hooks/use-click-outside/use-click-outside.d.ts +23 -1
- package/dist/src/hooks/use-debounce-callback/use-debounced-callback.d.ts +19 -1
- package/dist/src/hooks/use-debounce-value/use-debounced-value.d.ts +10 -1
- package/dist/src/hooks/use-disclosure/use-disclosure.d.ts +17 -8
- package/dist/src/hooks/use-document-title/use-document-title.d.ts +11 -0
- package/dist/src/hooks/use-event-listener/use-event-listener.d.ts +18 -1
- package/dist/src/hooks/use-hotkey/index.d.ts +2 -1
- package/dist/src/hooks/use-hotkey/use-hotkey.d.ts +62 -5
- package/dist/src/hooks/use-hotkey/utils/index.d.ts +4 -3
- package/dist/src/hooks/use-hotkey/utils/is-input-field.d.ts +12 -2
- package/dist/src/hooks/use-hotkey/utils/is-input-field.test.d.ts +1 -0
- package/dist/src/hooks/use-hotkey/utils/match-and-run.d.ts +36 -0
- package/dist/src/hooks/use-hotkey/utils/match-and-run.test.d.ts +1 -0
- package/dist/src/hooks/use-hotkey/utils/match-key-modifiers.d.ts +20 -6
- package/dist/src/hooks/use-hotkey/utils/match-key-modifiers.test.d.ts +1 -0
- package/dist/src/hooks/use-hover/use-hover.d.ts +8 -4
- package/dist/src/hooks/use-is-visible/use-is-visible.d.ts +28 -4
- package/dist/src/hooks/use-local-storage/use-local-storage.d.ts +13 -2
- package/dist/src/hooks/use-media-query/use-media-query.d.ts +10 -1
- package/dist/src/hooks/use-media-query/use-media-query.test.d.ts +1 -0
- package/dist/src/hooks/use-mutation/use-mutation.d.ts +18 -11
- package/dist/src/hooks/use-object/use-object.d.ts +15 -6
- package/dist/src/hooks/use-prevent-page-close/index.d.ts +1 -0
- package/dist/src/hooks/use-prevent-page-close/use-prevent-page-close.d.ts +10 -0
- package/dist/src/hooks/use-prevent-page-close/use-prevent-page-close.test.d.ts +1 -0
- package/dist/src/hooks/use-step/use-step.d.ts +18 -11
- package/dist/src/utils/form.d.ts +10 -0
- package/package.json +1 -1
- package/src/components/alert-dialog/alert-dialog.test.tsx +13 -9
- package/src/components/auto-complete/auto-complete.test.tsx +4 -14
- package/src/components/avatar/avatar.test.tsx +7 -12
- package/src/components/button/button.test.tsx +10 -15
- package/src/components/button/button.tsx +14 -9
- package/src/components/button/components/base-button.tsx +2 -4
- package/src/components/calendar/calendar.test.tsx +12 -19
- package/src/components/calendar/calendar.tsx +4 -0
- package/src/components/card/card.test.tsx +4 -6
- package/src/components/checkbox/checkbox.test.tsx +12 -8
- package/src/components/checkbox-group/checkbox-group.test.tsx +7 -8
- package/src/components/combobox/combobox.test.tsx +24 -21
- package/src/components/date-picker/date-input-form.test.tsx +77 -0
- package/src/components/date-picker/date-input.stories.tsx +30 -18
- package/src/components/date-picker/date-input.tsx +77 -44
- package/src/components/date-picker/date-picker.stories.tsx +31 -1
- package/src/components/date-picker/date-picker.test.tsx +3 -13
- package/src/components/date-picker/date-picker.tsx +35 -16
- package/src/components/date-picker/date-picker.utils.test.ts +32 -14
- package/src/components/date-picker/date-picker.utils.ts +33 -0
- package/src/components/date-picker/use-date-input-popover.test.ts +3 -1
- package/src/components/date-picker/use-hidden-field-value.ts +23 -0
- package/src/components/dialog/dialog.test.tsx +10 -8
- package/src/components/dropzone/dropzone.test.tsx +11 -13
- package/src/components/empty/empty.test.tsx +4 -3
- package/src/components/field/field.test.tsx +12 -13
- package/src/components/form/form.stories.tsx +16 -1
- package/src/components/index.ts +1 -0
- package/src/components/label/label.test.tsx +3 -3
- package/src/components/menu/menu.tsx +1 -5
- package/src/components/number-input/number-input.test.tsx +6 -2
- package/src/components/password/password.test.tsx +20 -6
- package/src/components/password/password.tsx +2 -2
- package/src/components/popover/popover.test.tsx +4 -4
- package/src/components/progress/progress.test.tsx +7 -8
- package/src/components/radio-group/radio-group.test.tsx +17 -11
- package/src/components/select/select.test.tsx +10 -10
- package/src/components/select/select.tsx +9 -1
- package/src/components/stepper/stepper.stories.tsx +11 -15
- package/src/components/stepper/stepper.test.tsx +6 -4
- package/src/components/switch/switch.test.tsx +3 -3
- package/src/components/table/table.test.tsx +9 -3
- package/src/components/tabs/tabs.test.tsx +6 -2
- package/src/components/tag/tag.test.tsx +1 -3
- package/src/components/textarea/textarea.test.tsx +4 -1
- package/src/components/timeline/timeline.test.tsx +10 -5
- package/src/components/toast/toast.test.tsx +11 -14
- package/src/components/tooltip/tooltip.test.tsx +1 -5
- package/src/components/tree/tree.test.tsx +3 -1
- package/src/hooks/index.ts +2 -2
- package/src/hooks/internal/index.ts +2 -0
- package/src/hooks/internal/is-apple-device.test.ts +41 -0
- package/src/hooks/internal/is-apple-device.ts +33 -0
- package/src/hooks/internal/use-isomorphic-layout-effect.ts +3 -1
- package/src/hooks/internal/use-latest-ref.ts +21 -0
- package/src/hooks/use-array/use-array.stories.tsx +435 -64
- package/src/hooks/use-array/use-array.test.tsx +398 -15
- package/src/hooks/use-array/use-array.ts +105 -66
- package/src/hooks/use-async/use-async.stories.tsx +255 -131
- package/src/hooks/use-async/use-async.test.ts +397 -0
- package/src/hooks/use-async/use-async.ts +117 -39
- package/src/hooks/use-boolean/index.ts +1 -0
- package/src/hooks/use-boolean/use-boolean.stories.tsx +377 -0
- package/src/hooks/use-boolean/use-boolean.test.tsx +177 -0
- package/src/hooks/use-boolean/use-boolean.ts +50 -0
- package/src/hooks/use-click-outside/use-click-outside.stories.tsx +188 -18
- package/src/hooks/use-click-outside/use-click-outside.test.tsx +89 -10
- package/src/hooks/use-click-outside/use-click-outside.ts +62 -16
- package/src/hooks/use-debounce-callback/use-debounced-callback.stories.tsx +141 -41
- package/src/hooks/use-debounce-callback/use-debounced-callback.test.ts +217 -9
- package/src/hooks/use-debounce-callback/use-debounced-callback.ts +71 -11
- package/src/hooks/use-debounce-value/use-debounced-value.stories.tsx +247 -47
- package/src/hooks/use-debounce-value/use-debounced-value.test.ts +105 -10
- package/src/hooks/use-debounce-value/use-debounced-value.ts +19 -10
- package/src/hooks/use-disclosure/use-disclosure.stories.tsx +305 -14
- package/src/hooks/use-disclosure/use-disclosure.test.ts +198 -50
- package/src/hooks/use-disclosure/use-disclosure.ts +49 -29
- package/src/hooks/use-document-title/use-document-title.stories.tsx +54 -0
- package/src/hooks/use-document-title/use-document-title.test.tsx +26 -0
- package/src/hooks/use-document-title/{use-document-title.tsx → use-document-title.ts} +17 -3
- package/src/hooks/use-event-listener/use-event-listener.stories.tsx +105 -9
- package/src/hooks/use-event-listener/use-event-listener.test.tsx +77 -10
- package/src/hooks/use-event-listener/use-event-listener.ts +71 -11
- package/src/hooks/use-focus-trap/use-focus-trap.test.ts +31 -6
- package/src/hooks/use-focus-trap/use-focus-trap.ts +3 -2
- package/src/hooks/use-hotkey/index.ts +9 -1
- package/src/hooks/use-hotkey/use-hotkey.stories.tsx +279 -74
- package/src/hooks/use-hotkey/use-hotkey.test.tsx +286 -34
- package/src/hooks/use-hotkey/use-hotkey.ts +141 -17
- package/src/hooks/use-hotkey/utils/index.ts +8 -3
- package/src/hooks/use-hotkey/utils/is-input-field.test.ts +78 -0
- package/src/hooks/use-hotkey/utils/is-input-field.ts +31 -10
- package/src/hooks/use-hotkey/utils/match-and-run.test.ts +203 -0
- package/src/hooks/use-hotkey/utils/match-and-run.ts +62 -0
- package/src/hooks/use-hotkey/utils/match-key-modifiers.test.ts +65 -0
- package/src/hooks/use-hotkey/utils/match-key-modifiers.ts +39 -12
- package/src/hooks/use-hover/use-hover.stories.tsx +258 -80
- package/src/hooks/use-hover/use-hover.test.tsx +266 -26
- package/src/hooks/use-hover/use-hover.tsx +93 -28
- package/src/hooks/use-is-visible/use-is-visible.stories.tsx +193 -46
- package/src/hooks/use-is-visible/use-is-visible.test.tsx +235 -7
- package/src/hooks/use-is-visible/use-is-visible.ts +114 -0
- package/src/hooks/use-local-storage/use-local-storage.stories.tsx +129 -29
- package/src/hooks/use-local-storage/use-local-storage.test.ts +106 -41
- package/src/hooks/use-local-storage/use-local-storage.ts +100 -31
- package/src/hooks/use-media-query/use-media-query.stories.tsx +86 -26
- package/src/hooks/use-media-query/use-media-query.test.ts +132 -0
- package/src/hooks/use-media-query/use-media-query.ts +39 -14
- package/src/hooks/use-memoized-fn/use-memoized-fn.ts +0 -1
- package/src/hooks/use-mutation/use-mutation.stories.tsx +260 -94
- package/src/hooks/use-mutation/use-mutation.test.ts +359 -0
- package/src/hooks/use-mutation/use-mutation.ts +97 -0
- package/src/hooks/use-object/use-object.stories.tsx +310 -79
- package/src/hooks/use-object/use-object.test.tsx +235 -56
- package/src/hooks/use-object/use-object.ts +59 -0
- package/src/hooks/use-pagination/use-pagination.tsx +0 -1
- package/src/hooks/use-prevent-page-close/index.ts +1 -0
- package/src/hooks/use-prevent-page-close/use-prevent-page-close.stories.tsx +39 -0
- package/src/hooks/use-prevent-page-close/use-prevent-page-close.test.ts +89 -0
- package/src/hooks/use-prevent-page-close/use-prevent-page-close.ts +27 -0
- package/src/hooks/use-range-pagination/use-range-pagination.test.tsx +1 -1
- package/src/hooks/use-range-pagination/use-range-pagination.tsx +1 -1
- package/src/hooks/use-selection/use-selection.ts +0 -1
- package/src/hooks/use-step/use-step.stories.tsx +178 -65
- package/src/hooks/use-step/use-step.test.ts +178 -53
- package/src/hooks/use-step/use-step.ts +57 -49
- package/src/utils/form.test.tsx +13 -8
- package/src/utils/form.tsx +10 -0
- package/src/utils/functions/getFormData.test.ts +1 -1
- package/dist/hooks/use-hotkey/utils/create-hotkey-listener.cjs.js +0 -1
- package/dist/hooks/use-hotkey/utils/create-hotkey-listener.es.js +0 -10
- package/dist/hooks/use-prevent-close-window/use-prevent-close-window.cjs.js +0 -1
- package/dist/hooks/use-prevent-close-window/use-prevent-close-window.es.js +0 -15
- package/dist/hooks/use-toggle/use-toggle.cjs.js +0 -1
- package/dist/hooks/use-toggle/use-toggle.es.js +0 -10
- package/dist/src/hooks/use-hotkey/utils/create-hotkey-listener.d.ts +0 -1
- package/dist/src/hooks/use-prevent-close-window/index.d.ts +0 -1
- package/dist/src/hooks/use-prevent-close-window/use-prevent-close-window.d.ts +0 -13
- package/dist/src/hooks/use-toggle/index.d.ts +0 -1
- package/dist/src/hooks/use-toggle/use-toggle.d.ts +0 -3
- package/src/hooks/use-async/use-async.test.tsx +0 -68
- package/src/hooks/use-hotkey/utils/create-hotkey-listener.ts +0 -25
- package/src/hooks/use-is-visible/use-is-visible.tsx +0 -49
- package/src/hooks/use-mutation/use-mutation.test.tsx +0 -83
- package/src/hooks/use-mutation/use-mutation.tsx +0 -59
- package/src/hooks/use-object/use-object.tsx +0 -46
- package/src/hooks/use-prevent-close-window/index.ts +0 -1
- package/src/hooks/use-prevent-close-window/use-prevent-close-window.stories.tsx +0 -32
- package/src/hooks/use-prevent-close-window/use-prevent-close-window.test.ts +0 -79
- package/src/hooks/use-prevent-close-window/use-prevent-close-window.ts +0 -33
- package/src/hooks/use-toggle/index.ts +0 -1
- package/src/hooks/use-toggle/use-toggle.stories.tsx +0 -25
- package/src/hooks/use-toggle/use-toggle.test.tsx +0 -64
- package/src/hooks/use-toggle/use-toggle.ts +0 -14
- /package/dist/src/{hooks/use-prevent-close-window/use-prevent-close-window.test.d.ts → components/date-picker/date-input-form.test.d.ts} +0 -0
- /package/dist/src/hooks/{use-toggle/use-toggle.test.d.ts → internal/is-apple-device.test.d.ts} +0 -0
|
@@ -1,8 +1,54 @@
|
|
|
1
|
-
import { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
-
import {
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { type ReactNode, useState } from "react";
|
|
3
|
+
import { Kbd, KbdGroup } from "../../components";
|
|
3
4
|
import { cn } from "../../lib";
|
|
4
|
-
import { useHotkey } from "../use-hotkey";
|
|
5
|
+
import { getHotkeyHandler, useHotkey } from "../use-hotkey";
|
|
5
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Bind keyboard shortcuts to handlers. `useHotkey` registers on `document`, so
|
|
9
|
+
* shortcuts fire regardless of which element has focus; `getHotkeyHandler`
|
|
10
|
+
* builds an element-scoped `onKeyDown` handler instead.
|
|
11
|
+
*
|
|
12
|
+
* **Two forms of `useHotkey`:**
|
|
13
|
+
* - Single binding — `useHotkey(keys, handler, options?)`. `keys` is a string
|
|
14
|
+
* or string array sharing one handler (the handler fires if any matches).
|
|
15
|
+
* - Multiple bindings — `useHotkey(bindings)` where `bindings` is a list of
|
|
16
|
+
* `[key, handler, options?]` tuples, each dispatched independently with its
|
|
17
|
+
* own options.
|
|
18
|
+
*
|
|
19
|
+
* ```tsx
|
|
20
|
+
* useHotkey("mod+k", openPalette);
|
|
21
|
+
* useHotkey(["w", "ArrowUp"], moveUp);
|
|
22
|
+
* useHotkey([
|
|
23
|
+
* ["mod+k", openPalette],
|
|
24
|
+
* ["mod+s", save, { preventDefault: false }],
|
|
25
|
+
* ]);
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* **Key syntax** — zero or more modifiers followed by the main key, joined with
|
|
29
|
+
* `+` (e.g. `"ctrl+shift+k"`). Literal modifiers are `shift`, `ctrl`, `alt`,
|
|
30
|
+
* `meta`. `mod` is platform-aware: it resolves to `meta` (⌘) on Apple devices
|
|
31
|
+
* and `ctrl` everywhere else — write the shortcut once and it adapts.
|
|
32
|
+
*
|
|
33
|
+
* **Matching is strict and exact.** A binding fires only when every required
|
|
34
|
+
* modifier is held and no other modifier is. `"a"` will not fire for `Ctrl+A`;
|
|
35
|
+
* `"ctrl+k"` will not fire for `Ctrl+Shift+K`. Strict `mod` also rejects the
|
|
36
|
+
* opposite platform's modifier (on Apple, `mod+k` matches ⌘+K but not Ctrl+K).
|
|
37
|
+
*
|
|
38
|
+
* **`getHotkeyHandler(bindings)`** returns an `onKeyDown` handler for a single
|
|
39
|
+
* element. It shares the same strict-modifier and `mod` behavior but applies no
|
|
40
|
+
* tag suppression — it has no `tagsToIgnore` and matches regardless of the
|
|
41
|
+
* target tag, including the `<input>` it is attached to. Use it for shortcuts
|
|
42
|
+
* that must work while a specific field is focused (e.g. ⌘/Ctrl+Enter to
|
|
43
|
+
* submit).
|
|
44
|
+
*
|
|
45
|
+
* ```tsx
|
|
46
|
+
* <input onKeyDown={getHotkeyHandler("mod+enter", submit)} />
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* Handlers are read through a ref, so passing an inline function never
|
|
50
|
+
* re-attaches the listener. SSR-safe: no DOM access on the server.
|
|
51
|
+
*/
|
|
6
52
|
const meta: Meta = {
|
|
7
53
|
title: "hooks/useHotkey",
|
|
8
54
|
};
|
|
@@ -10,6 +56,17 @@ const meta: Meta = {
|
|
|
10
56
|
export default meta;
|
|
11
57
|
type Story = StoryObj<typeof meta>;
|
|
12
58
|
|
|
59
|
+
const isApple =
|
|
60
|
+
typeof navigator !== "undefined" &&
|
|
61
|
+
/Mac|iP(hone|ad|od)/.test(navigator.platform);
|
|
62
|
+
const modLabel = isApple ? "⌘" : "Ctrl";
|
|
63
|
+
|
|
64
|
+
const Stage = ({ children }: { children: ReactNode }) => (
|
|
65
|
+
<div className="flex h-52 w-80 items-center justify-center overflow-hidden rounded-md border border-input">
|
|
66
|
+
{children}
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
|
|
13
70
|
const generateRandomColor = () => {
|
|
14
71
|
const letters = "0123456789ABCDEF";
|
|
15
72
|
let color = "#";
|
|
@@ -19,78 +76,31 @@ const generateRandomColor = () => {
|
|
|
19
76
|
return color;
|
|
20
77
|
};
|
|
21
78
|
|
|
22
|
-
const Kbd = (props: HTMLProps<HTMLDivElement>) => (
|
|
23
|
-
<kbd
|
|
24
|
-
{...props}
|
|
25
|
-
className="inline-block px-3 py-1 text-sm font-semibold text-gray-800 bg-gray-200 border rounded-md shadow-inner border-input"
|
|
26
|
-
/>
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
const Wrapper = ({ children }: { children: ReactNode }) => (
|
|
30
|
-
<div>
|
|
31
|
-
<div className="space-y-2">
|
|
32
|
-
<div className="flex gap-2">
|
|
33
|
-
<span>Usa</span>
|
|
34
|
-
<Kbd>C</Kbd>
|
|
35
|
-
<span>+</span>
|
|
36
|
-
<Kbd>R</Kbd>
|
|
37
|
-
<span>para cambiar el color</span>
|
|
38
|
-
</div>
|
|
39
|
-
<div className="flex gap-2">
|
|
40
|
-
<span>Usa</span>
|
|
41
|
-
<Kbd>w</Kbd>
|
|
42
|
-
<Kbd>a</Kbd>
|
|
43
|
-
<Kbd>s</Kbd>
|
|
44
|
-
<Kbd>d</Kbd>
|
|
45
|
-
<span>o</span>
|
|
46
|
-
<Kbd>↑</Kbd>
|
|
47
|
-
<Kbd>←</Kbd>
|
|
48
|
-
<Kbd>↓</Kbd>
|
|
49
|
-
<Kbd>→</Kbd>
|
|
50
|
-
<span>Para moverte</span>
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
<div className="flex items-center justify-center w-80 h-52">{children}</div>
|
|
54
|
-
</div>
|
|
55
|
-
);
|
|
56
|
-
|
|
57
79
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
* // si se debe prevenir los comandos por defecto del navegador
|
|
66
|
-
* preventDefault?: boolean;
|
|
67
|
-
* @default true
|
|
68
|
-
* // si los comandos se deberían ejecutar mientras el usuario está escribiendo en un input o textarea
|
|
69
|
-
* ignoreInputFields?: boolean;
|
|
70
|
-
* @default false
|
|
71
|
-
* };
|
|
72
|
-
* ```
|
|
73
|
-
**/
|
|
74
|
-
export const Default: Story = {
|
|
80
|
+
* Interactive demo combining several bindings:
|
|
81
|
+
* - `Ctrl+B` randomizes the color.
|
|
82
|
+
* - `Space` pulses a ring.
|
|
83
|
+
* - `WASD` or the arrow keys move the dot, using an array of keys on a single
|
|
84
|
+
* handler that branches on `event.key`.
|
|
85
|
+
*/
|
|
86
|
+
export const Playground: Story = {
|
|
75
87
|
render: () => {
|
|
76
88
|
const [posX, setPosX] = useState(0);
|
|
77
89
|
const [posY, setPosY] = useState(0);
|
|
78
90
|
const [color, setColor] = useState("#333333");
|
|
79
|
-
const [
|
|
91
|
+
const [pulse, setPulse] = useState(false);
|
|
80
92
|
|
|
81
|
-
useHotkey(
|
|
93
|
+
useHotkey("ctrl+b", () => setColor(generateRandomColor()));
|
|
82
94
|
|
|
83
95
|
useHotkey(" ", () => {
|
|
84
|
-
|
|
85
|
-
setTimeout(() =>
|
|
86
|
-
setSpacePressed(false);
|
|
87
|
-
}, 300);
|
|
96
|
+
setPulse(true);
|
|
97
|
+
setTimeout(() => setPulse(false), 300);
|
|
88
98
|
});
|
|
89
99
|
|
|
90
100
|
useHotkey(
|
|
91
101
|
["w", "s", "a", "d", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"],
|
|
92
|
-
(
|
|
93
|
-
const { key } =
|
|
102
|
+
(event) => {
|
|
103
|
+
const { key } = event;
|
|
94
104
|
if (["w", "ArrowUp"].includes(key)) return setPosY((y) => y - 20);
|
|
95
105
|
if (["s", "ArrowDown"].includes(key)) return setPosY((y) => y + 20);
|
|
96
106
|
if (["a", "ArrowLeft"].includes(key)) return setPosX((x) => x - 20);
|
|
@@ -99,18 +109,213 @@ export const Default: Story = {
|
|
|
99
109
|
);
|
|
100
110
|
|
|
101
111
|
return (
|
|
102
|
-
<
|
|
103
|
-
<div
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
+
<div className="space-y-4">
|
|
113
|
+
<div className="flex flex-wrap items-center gap-2 text-sm">
|
|
114
|
+
<KbdGroup>
|
|
115
|
+
<Kbd>Ctrl</Kbd>
|
|
116
|
+
<Kbd>B</Kbd>
|
|
117
|
+
</KbdGroup>
|
|
118
|
+
color · <Kbd>Space</Kbd> pulse ·{" "}
|
|
119
|
+
<KbdGroup>
|
|
120
|
+
<Kbd>W</Kbd>
|
|
121
|
+
<Kbd>A</Kbd>
|
|
122
|
+
<Kbd>S</Kbd>
|
|
123
|
+
<Kbd>D</Kbd>
|
|
124
|
+
</KbdGroup>
|
|
125
|
+
move
|
|
126
|
+
</div>
|
|
127
|
+
<Stage>
|
|
128
|
+
<div
|
|
129
|
+
className={cn(
|
|
130
|
+
"h-10 w-10 rounded-full transition-transform",
|
|
131
|
+
pulse && "ring ring-offset-2",
|
|
132
|
+
)}
|
|
133
|
+
style={{
|
|
134
|
+
backgroundColor: color,
|
|
135
|
+
transform: `translate(${posX}px, ${posY}px)`,
|
|
136
|
+
}}
|
|
137
|
+
/>
|
|
138
|
+
</Stage>
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Modifier matching is exact: `"ctrl+m"` fires only when Ctrl is held and
|
|
146
|
+
* Shift/Alt/Meta are not. Try pressing `M` alone or `Ctrl+Shift+M` — neither
|
|
147
|
+
* matches.
|
|
148
|
+
*/
|
|
149
|
+
export const KeyCombo: Story = {
|
|
150
|
+
render: () => {
|
|
151
|
+
const [count, setCount] = useState(0);
|
|
152
|
+
useHotkey("ctrl+m", () => setCount((c) => c + 1));
|
|
153
|
+
return (
|
|
154
|
+
<p className="text-sm">
|
|
155
|
+
Press <Kbd>Ctrl</Kbd> + <Kbd>M</Kbd> — matched {count} time(s)
|
|
156
|
+
</p>
|
|
157
|
+
);
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* `mod` is platform-aware: it resolves to `meta` (⌘) on Apple devices and
|
|
163
|
+
* `ctrl` everywhere else, so one binding works cross-platform. Matching stays
|
|
164
|
+
* strict — on a Mac, `mod+m` matches ⌘+M but not Ctrl+M.
|
|
165
|
+
*/
|
|
166
|
+
export const Mod: Story = {
|
|
167
|
+
render: () => {
|
|
168
|
+
const [count, setCount] = useState(0);
|
|
169
|
+
useHotkey("mod+m", () => setCount((c) => c + 1));
|
|
170
|
+
return (
|
|
171
|
+
<p className="text-sm">
|
|
172
|
+
Press <Kbd>{modLabel}</Kbd> + <Kbd>M</Kbd> — matched {count} time(s)
|
|
173
|
+
</p>
|
|
174
|
+
);
|
|
175
|
+
},
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* The array-of-tuples form binds several independent shortcuts in one call.
|
|
180
|
+
* Each `[key, handler, options?]` tuple dispatches on its own and can override
|
|
181
|
+
* options per binding (here `mod+s` opts out of `preventDefault`).
|
|
182
|
+
*/
|
|
183
|
+
export const MultipleBindings: Story = {
|
|
184
|
+
render: () => {
|
|
185
|
+
const [log, setLog] = useState<string[]>([]);
|
|
186
|
+
const push = (entry: string) => setLog((l) => [entry, ...l].slice(0, 5));
|
|
187
|
+
|
|
188
|
+
useHotkey([
|
|
189
|
+
["mod+m", () => push("palette")],
|
|
190
|
+
["mod+s", () => push("save"), { preventDefault: false }],
|
|
191
|
+
]);
|
|
192
|
+
|
|
193
|
+
return (
|
|
194
|
+
<div className="space-y-3 text-sm">
|
|
195
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
196
|
+
<KbdGroup>
|
|
197
|
+
<Kbd>{modLabel}</Kbd>
|
|
198
|
+
<Kbd>M</Kbd>
|
|
199
|
+
</KbdGroup>
|
|
200
|
+
palette ·
|
|
201
|
+
<KbdGroup>
|
|
202
|
+
<Kbd>{modLabel}</Kbd>
|
|
203
|
+
<Kbd>S</Kbd>
|
|
204
|
+
</KbdGroup>
|
|
205
|
+
save
|
|
206
|
+
</div>
|
|
207
|
+
<ul className="font-mono text-muted-foreground">
|
|
208
|
+
{log.map((entry, i) => (
|
|
209
|
+
<li key={`${entry}-${i}`}>{entry}</li>
|
|
210
|
+
))}
|
|
211
|
+
</ul>
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* By default `useHotkey` skips events while a control in `tagsToIgnore`
|
|
219
|
+
* (`INPUT`, `TEXTAREA`, `SELECT`) is focused. Focus the input and press `S` —
|
|
220
|
+
* the counter stays put. Pass `tagsToIgnore: []` to let shortcuts fire during
|
|
221
|
+
* text entry, or `triggerOnContentEditable: true` for contentEditable regions.
|
|
222
|
+
*/
|
|
223
|
+
export const TagsToIgnore: Story = {
|
|
224
|
+
render: () => {
|
|
225
|
+
const [ignored, setIgnored] = useState(0);
|
|
226
|
+
const [always, setAlways] = useState(0);
|
|
227
|
+
useHotkey("s", () => setIgnored((c) => c + 1));
|
|
228
|
+
useHotkey("d", () => setAlways((c) => c + 1), { tagsToIgnore: [] });
|
|
229
|
+
return (
|
|
230
|
+
<div className="space-y-3 text-sm">
|
|
231
|
+
<p>
|
|
232
|
+
<Kbd>S</Kbd> (default) — matched {ignored} · <Kbd>D</Kbd>{" "}
|
|
233
|
+
(tagsToIgnore: []) — matched {always}
|
|
234
|
+
</p>
|
|
235
|
+
<input
|
|
236
|
+
placeholder="type here — S won't fire, D will"
|
|
237
|
+
className="w-full rounded-md border border-input px-3 py-1"
|
|
238
|
+
/>
|
|
239
|
+
</div>
|
|
240
|
+
);
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* `getHotkeyHandler` builds an element-scoped `onKeyDown` handler. Unlike
|
|
246
|
+
* `useHotkey`, it applies no tag suppression, so it fires even on the `<input>`
|
|
247
|
+
* it is attached to — the canonical case for a submit shortcut while a field is
|
|
248
|
+
* focused. Press ⌘/Ctrl+Enter inside the input to submit.
|
|
249
|
+
*/
|
|
250
|
+
export const ElementScoped: Story = {
|
|
251
|
+
render: () => {
|
|
252
|
+
const [value, setValue] = useState("");
|
|
253
|
+
const [submitted, setSubmitted] = useState<string | null>(null);
|
|
254
|
+
return (
|
|
255
|
+
<div className="space-y-3 text-sm">
|
|
256
|
+
<input
|
|
257
|
+
value={value}
|
|
258
|
+
onChange={(e) => setValue(e.target.value)}
|
|
259
|
+
onKeyDown={getHotkeyHandler("mod+enter", () => {
|
|
260
|
+
setSubmitted(value);
|
|
261
|
+
setValue("");
|
|
262
|
+
})}
|
|
263
|
+
placeholder="type, then press mod+Enter"
|
|
264
|
+
className="w-full rounded-md border border-input px-3 py-1"
|
|
112
265
|
/>
|
|
113
|
-
|
|
266
|
+
<p className="flex flex-wrap items-center gap-2">
|
|
267
|
+
<KbdGroup>
|
|
268
|
+
<Kbd>{modLabel}</Kbd>
|
|
269
|
+
<Kbd>Enter</Kbd>
|
|
270
|
+
</KbdGroup>
|
|
271
|
+
to submit — last: {submitted ?? "—"}
|
|
272
|
+
</p>
|
|
273
|
+
</div>
|
|
274
|
+
);
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* `watch: false` disables the binding without unmounting. Toggle it to start
|
|
280
|
+
* and stop listening at runtime.
|
|
281
|
+
*/
|
|
282
|
+
export const Disabled: Story = {
|
|
283
|
+
render: () => {
|
|
284
|
+
const [enabled, setEnabled] = useState(false);
|
|
285
|
+
const [count, setCount] = useState(0);
|
|
286
|
+
useHotkey("e", () => setCount((c) => c + 1), {
|
|
287
|
+
watch: enabled,
|
|
288
|
+
});
|
|
289
|
+
return (
|
|
290
|
+
<div className="space-y-3 text-sm">
|
|
291
|
+
<label className="flex items-center gap-2">
|
|
292
|
+
<input
|
|
293
|
+
type="checkbox"
|
|
294
|
+
checked={enabled}
|
|
295
|
+
onChange={(e) => setEnabled(e.target.checked)}
|
|
296
|
+
/>
|
|
297
|
+
binding enabled
|
|
298
|
+
</label>
|
|
299
|
+
<p>
|
|
300
|
+
Press <Kbd>e</Kbd> — matched {count} time(s)
|
|
301
|
+
</p>
|
|
302
|
+
</div>
|
|
303
|
+
);
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Bind to `keyup` instead of the default `keydown` via the `eventName` option
|
|
309
|
+
* — the handler fires when the key is released.
|
|
310
|
+
*/
|
|
311
|
+
export const OnKeyUp: Story = {
|
|
312
|
+
render: () => {
|
|
313
|
+
const [count, setCount] = useState(0);
|
|
314
|
+
useHotkey("u", () => setCount((c) => c + 1), { eventName: "keyup" });
|
|
315
|
+
return (
|
|
316
|
+
<p className="text-sm">
|
|
317
|
+
Release <Kbd>U</Kbd> — matched {count} time(s)
|
|
318
|
+
</p>
|
|
114
319
|
);
|
|
115
320
|
},
|
|
116
321
|
};
|