@enigmax/primitives 0.22.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/{chunk-QQFNAKMY.js → chunk-45UHLZYT.js} +1 -1
  2. package/dist/chunk-4VUHQFAT.js +130 -0
  3. package/dist/{chunk-D5A2ZMAG.js → chunk-4ZPMP47J.js} +6 -1
  4. package/dist/chunk-DZMHY3SU.js +777 -0
  5. package/dist/{chunk-FWVWX67R.js → chunk-FGBIZDV2.js} +3 -3
  6. package/dist/chunk-N6PDHMAX.js +213 -0
  7. package/dist/chunk-NONGREXC.js +248 -0
  8. package/dist/{chunk-R4ZAEE7V.js → chunk-QJJ34E4G.js} +34 -10
  9. package/dist/chunk-S2FPN5ID.js +115 -0
  10. package/dist/chunk-S7EE57YB.js +9 -0
  11. package/dist/chunk-VKL3DEIQ.js +804 -0
  12. package/dist/chunk-WSQC3PCC.js +466 -0
  13. package/dist/chunk-Z3VDE7OA.js +532 -0
  14. package/dist/clipboard-menu-B_ouitfS.d.ts +264 -0
  15. package/dist/color-D_rZ83Oc.d.ts +152 -0
  16. package/dist/color-OPV3BJV6.js +452 -0
  17. package/dist/{index-BHpOZncw.d.ts → index-ZPvlf9vs.d.ts} +53 -5
  18. package/dist/index.d.ts +6 -2
  19. package/dist/index.js +9 -4
  20. package/dist/keys-D2zJs1uB.d.ts +100 -0
  21. package/dist/next/index.d.ts +10 -3
  22. package/dist/next/index.js +22 -13
  23. package/dist/react/context-menu.d.ts +219 -0
  24. package/dist/react/context-menu.js +6 -0
  25. package/dist/react/index.d.ts +12 -5
  26. package/dist/react/index.js +21 -12
  27. package/dist/react/input.d.ts +3 -3
  28. package/dist/react/input.js +2 -1
  29. package/dist/react/palette.d.ts +1 -1
  30. package/dist/react/palette.js +3 -3
  31. package/dist/react/search.d.ts +1 -1
  32. package/dist/react/search.js +2 -2
  33. package/dist/react/select.d.ts +217 -0
  34. package/dist/react/select.js +7 -0
  35. package/dist/react/selection.d.ts +104 -0
  36. package/dist/react/selection.js +4 -0
  37. package/dist/react-router/index.d.ts +10 -3
  38. package/dist/react-router/index.js +22 -13
  39. package/dist/search/index.d.ts +2 -2
  40. package/dist/search/index.js +1 -1
  41. package/dist/{search-BD9-5O5U.d.ts → search-DYgqRp37.d.ts} +13 -1
  42. package/dist/{search-UQEXAPQB.js → search-PBZORZ7P.js} +1 -1
  43. package/dist/select-ClSy-J1f.d.ts +100 -0
  44. package/dist/selection-B_pmzHpy.d.ts +150 -0
  45. package/package.json +23 -2
  46. package/recipes/color/styles.css +143 -0
  47. package/recipes/context-menu/styles.css +185 -0
  48. package/recipes/input/styles.css +9 -0
  49. package/recipes/palette/styles.css +3 -0
  50. package/recipes/search/tailwind.tsx +3 -2
  51. package/recipes/select/styles.css +230 -0
  52. package/recipes/toast/styles.css +1 -1
  53. package/registry.json +432 -5
  54. package/src/core/clipboard-menu.ts +270 -0
  55. package/src/core/color.ts +248 -0
  56. package/src/core/context-menu.ts +694 -0
  57. package/src/core/keys.ts +264 -0
  58. package/src/core/search.ts +19 -0
  59. package/src/core/select.ts +404 -0
  60. package/src/core/selection.ts +648 -0
  61. package/src/index.ts +92 -1
  62. package/src/react/context-menu/context.ts +64 -0
  63. package/src/react/context-menu/index.tsx +108 -0
  64. package/src/react/context-menu/root.tsx +970 -0
  65. package/src/react/context-menu/styles.ts +194 -0
  66. package/src/react/index.ts +100 -1
  67. package/src/react/input/color-styles.ts +156 -0
  68. package/src/react/input/color.tsx +466 -0
  69. package/src/react/input/index.tsx +54 -6
  70. package/src/react/input/types.ts +59 -3
  71. package/src/react/palette/root.tsx +2 -2
  72. package/src/react/select/context.ts +56 -0
  73. package/src/react/select/index.tsx +96 -0
  74. package/src/react/select/root.tsx +839 -0
  75. package/src/react/select/styles.ts +238 -0
  76. package/src/react/selection/index.tsx +115 -0
  77. package/src/react/selection/use-selection.ts +260 -0
  78. package/dist/password-C8lG4Zm9.d.ts +0 -71
  79. /package/dist/{chunk-U3V4EHOB.js → chunk-MSOCCQGH.js} +0 -0
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { createMarquee, type MarqueeOptions, type MarqueeInstance, type MarqueeHover } from "@/core/marquee";
2
2
  export { createInput, type InputOptions, type InputInstance, type InputAction, type InputIcon, type InputActionState } from "@/core/input";
3
- export { createSearch, subsequenceMatcher, type SearchOptions, type SearchInstance, type SearchMatch, type FuseConstructor, type FuseLike } from "@/core/search";
3
+ export { createSearch, subsequenceMatcher, shortenQuery, type SearchOptions, type SearchInstance, type SearchMatch, type FuseConstructor, type FuseLike } from "@/core/search";
4
4
  export { createButton, type ButtonOptions, type ButtonInstance, type ButtonState, type ButtonElement, type ButtonCooldown } from "@/core/button";
5
5
  export { INPUT_ICON_PATHS, iconMarkup } from "@/core/input";
6
6
  export {
@@ -12,6 +12,23 @@ export {
12
12
  type PasswordStrengthReport,
13
13
  type PasswordScore
14
14
  } from "@/core/password";
15
+ // The colour maths behind `<Input type="color">`. The picker is React, the conversions are
16
+ // not, so a vanilla page drawing its own square has the same parser and the same hue rule.
17
+ export {
18
+ parseColor,
19
+ formatColor,
20
+ toHex,
21
+ rgbToHsv,
22
+ hsvToRgb,
23
+ rgbToHsl,
24
+ hslToRgb,
25
+ colorEquals,
26
+ type Rgb,
27
+ type Hsv,
28
+ type Hsl,
29
+ type ColorFormat,
30
+ type FormatColorOptions
31
+ } from "@/core/color";
15
32
  export { createNotifications, type Notifications, type Notification, type NotificationInput, type NotificationTone, type NotificationAction, type NotificationsOptions, type PromiseMessages } from "@/core/notifications";
16
33
  export { createNetworkMonitor, SERVER_NETWORK_STATE, type NetworkState, type NetworkMonitor } from "@/core/network";
17
34
  export {
@@ -61,3 +78,77 @@ export {
61
78
  type PositionedRow,
62
79
  type PaletteKey
63
80
  } from "@/core/palette";
81
+ // The select's non-rendering half, for the same reason as the palette's: the selection
82
+ // rules, the filter and the highlight are arithmetic, not React.
83
+ export {
84
+ createSelect,
85
+ SELECT_SEARCH_KEYS,
86
+ type SelectOption,
87
+ type SelectOptions,
88
+ type SelectInstance,
89
+ type SelectState,
90
+ type SelectMoveKey
91
+ } from "@/core/select";
92
+ // The context menu's non-rendering half: the tree, the highlight, the filter and the cache
93
+ // of a fetched submenu are arithmetic, so a renderer that is not React only has to draw.
94
+ export {
95
+ createContextMenu,
96
+ isAction,
97
+ CONTEXT_MENU_SEARCH_KEYS,
98
+ type ContextMenuEntry,
99
+ type ContextMenuAction,
100
+ type ContextMenuSeparator,
101
+ type ContextMenuLabel,
102
+ type ContextMenuOptions,
103
+ type ContextMenuInstance,
104
+ type ContextMenuState,
105
+ type ContextMenuLevel,
106
+ type ContextMenuPoint,
107
+ type ContextMenuMoveKey
108
+ } from "@/core/context-menu";
109
+ // Copy, Cut and Paste for a menu, and the DOM questions behind them: what is selected,
110
+ // whether it can be written to, and how text goes back in without breaking undo.
111
+ export {
112
+ CLIPBOARD_PREFIX,
113
+ clipboardEntries,
114
+ clipboardAction,
115
+ clipboardHasText,
116
+ inspectClipboardTarget,
117
+ performClipboardAction,
118
+ type ClipboardAction,
119
+ type ClipboardTarget,
120
+ type ClipboardMenuOptions,
121
+ type ClipboardMenuLabels
122
+ } from "@/core/clipboard-menu";
123
+
124
+ // The selection model, for the same reason: what a Ctrl+click does to a set is not React.
125
+ export {
126
+ createSelection,
127
+ DEFAULT_SELECTION_SHORTCUTS,
128
+ type SelectionCommand,
129
+ type SelectionCommandName,
130
+ type SelectionCommandEvent,
131
+ type SelectionShortcuts,
132
+ type SelectionOptions,
133
+ type SelectionInstance,
134
+ type SelectionState,
135
+ type SelectionClickModifiers
136
+ } from "@/core/selection";
137
+ // Shortcuts as data, shared by the two above: the menu PRINTS what the list LISTENS for, and
138
+ // a menu whose label says Ctrl+A over a list bound to Cmd+A is the defect that splitting them
139
+ // produces.
140
+ export {
141
+ parseShortcut,
142
+ matchesShortcut,
143
+ shortcutTokens,
144
+ shortcutText,
145
+ shortcutList,
146
+ isApplePlatform,
147
+ typeaheadStep,
148
+ TYPEAHEAD_MS,
149
+ type Shortcut,
150
+ type ShortcutSpec,
151
+ type ShortcutEvent,
152
+ type TypeaheadState,
153
+ type TypeaheadStep
154
+ } from "@/core/keys";
@@ -0,0 +1,64 @@
1
+ "use client";
2
+
3
+ import { createContext, useContext } from "react";
4
+ import type { ReactNode, RefObject } from "react";
5
+ import type { ContextMenuAction, ContextMenuEntry, ContextMenuInstance, ContextMenuPoint, ContextMenuState } from "@/core/context-menu";
6
+
7
+ /**
8
+ * One row, as React draws it. The core carries `icon` as `unknown` because it cannot know
9
+ * what a node is; here it is one.
10
+ */
11
+ export interface ContextMenuItem extends Omit<ContextMenuAction, "icon" | "items"> {
12
+ icon?: ReactNode;
13
+ items?: readonly ContextMenuNode[];
14
+ }
15
+
16
+ /** Anything that can appear in a menu, with React's icon type. */
17
+ export type ContextMenuNode = ContextMenuItem | Exclude<ContextMenuEntry, ContextMenuAction>;
18
+
19
+ export interface ContextMenuContextValue {
20
+ instance: ContextMenuInstance;
21
+ state: ContextMenuState;
22
+ /**
23
+ * Open at a point, over what the press landed on. False when there was nothing to show,
24
+ * so the press is left alone.
25
+ *
26
+ * `target` is what the clipboard rows are built from - the field or the selection under
27
+ * the pointer - and a caller that leaves it out gets a menu with none of them, which is
28
+ * the right answer for a menu opened from somewhere that is not a press.
29
+ */
30
+ open: (point: ContextMenuPoint, target?: EventTarget | null) => boolean;
31
+ close: () => void;
32
+ /** ids the parts need to point at each other. */
33
+ ids: { trigger: string; };
34
+ panelId: (level: number) => string;
35
+ itemId: (level: number, index: number) => string;
36
+ triggerRef: RefObject<HTMLElement | null>;
37
+ /** Which panel the keyboard is in. Always the deepest open one. */
38
+ onMenuKeyDown: (event: React.KeyboardEvent) => void;
39
+ /** How long the pointer must rest on a row before its submenu opens, and before one closes. */
40
+ delays: { open: number; close: number; };
41
+ /** Drop the pending close, wherever it was scheduled. Called wherever the pointer lands. */
42
+ cancelClose: () => void;
43
+ /** Close everything below `level`, after the beat - replacing any close already waiting. */
44
+ scheduleClose: (level: number) => void;
45
+ /** Draw a row instead of the default icon / label / shortcut. */
46
+ renderItem?: (item: ContextMenuItem, level: number, index: number) => ReactNode;
47
+ /** Shown while a fetched submenu has not answered. */
48
+ loadingLabel: ReactNode;
49
+ /** Shown when a filter matched nothing, or a fetched submenu came back with nothing. */
50
+ emptyLabel: ReactNode;
51
+ }
52
+
53
+ /**
54
+ * Null rather than a default: a part rendered outside its Root is a mistake with an obvious
55
+ * fix, and a silent default would leave it half-working - a trigger that opens nothing, a row
56
+ * that invokes into the void.
57
+ */
58
+ export const ContextMenuContext = createContext<ContextMenuContextValue | null>(null);
59
+
60
+ export function useContextMenuContext(part: string): ContextMenuContextValue {
61
+ const value = useContext(ContextMenuContext);
62
+ if (!value) throw new Error(`<${part}> must be rendered inside <ContextMenu.Root>.`);
63
+ return value;
64
+ }
@@ -0,0 +1,108 @@
1
+ "use client";
2
+
3
+ import type { ReactNode } from "react";
4
+ import * as parts from "@/react/context-menu/root";
5
+ import { useContextMenuContext } from "@/react/context-menu/context";
6
+ import type { ContextMenuRootProps, ContextMenuTriggerProps, ContextMenuContentProps } from "@/react/context-menu/root";
7
+
8
+ export type ContextMenuProps = ContextMenuRootProps & {
9
+ /** How many rows to put in the document at once. `Infinity` renders the lot. */
10
+ chunk?: ContextMenuContentProps["chunk"];
11
+ /** Open on a long press as well, which is what a right-click is on a touch screen. */
12
+ longPress?: boolean;
13
+ triggerProps?: ContextMenuTriggerProps;
14
+ contentProps?: ContextMenuContentProps;
15
+ };
16
+
17
+ /**
18
+ * The context menu, assembled.
19
+ *
20
+ * ```tsx
21
+ * <ContextMenu
22
+ * title={file.name}
23
+ * items={[
24
+ * { id: "rename", label: "Rename", shortcut: "F2", icon: <Pencil /> },
25
+ * { type: "separator" },
26
+ * { id: "delete", label: "Delete", shortcut: "Delete", destructive: true }
27
+ * ]}
28
+ * onSelect={(item) => run(item.id)}
29
+ * >
30
+ * <FileRow file={file} />
31
+ * </ContextMenu>
32
+ * ```
33
+ *
34
+ * Everything it renders is a part you can render yourself instead - see `ContextMenu.Root`
35
+ * for the anatomy. This is those parts in the order they belong in, not a different
36
+ * component: the children become the area a right-click opens the menu over.
37
+ *
38
+ * A menu with no rows to show does not open, and the press falls through to the browser's own
39
+ * menu - an empty box at the pointer tells the reader nothing, and swallowing the press to
40
+ * show one tells them less.
41
+ */
42
+ export function ContextMenu(props: ContextMenuProps): ReactNode {
43
+ const { chunk, longPress, triggerProps, contentProps, children, ...rest } = props;
44
+
45
+ return (
46
+ <parts.ContextMenuRoot {...rest}>
47
+ <parts.ContextMenuTrigger longPress={longPress} {...triggerProps}>{children}</parts.ContextMenuTrigger>
48
+ <parts.ContextMenuContent chunk={chunk} {...contentProps} />
49
+ </parts.ContextMenuRoot>
50
+ );
51
+ }
52
+
53
+ ContextMenu.Root = parts.ContextMenuRoot;
54
+ ContextMenu.Trigger = parts.ContextMenuTrigger;
55
+ ContextMenu.Content = parts.ContextMenuContent;
56
+ ContextMenu.Panel = parts.ContextMenuPanel;
57
+ ContextMenu.Search = parts.ContextMenuSearch;
58
+ ContextMenu.List = parts.ContextMenuList;
59
+ ContextMenu.Item = parts.ContextMenuRow;
60
+
61
+ export {
62
+ ContextMenuRoot,
63
+ ContextMenuTrigger,
64
+ ContextMenuContent,
65
+ ContextMenuPanel,
66
+ ContextMenuSearch,
67
+ ContextMenuList,
68
+ ContextMenuRow,
69
+ type ContextMenuRootProps,
70
+ type ContextMenuTriggerProps,
71
+ type ContextMenuContentProps,
72
+ type ContextMenuPanelProps,
73
+ type ContextMenuSearchProps,
74
+ type ContextMenuListProps,
75
+ type ContextMenuRowProps
76
+ } from "@/react/context-menu/root";
77
+ export { useContextMenuContext, type ContextMenuItem, type ContextMenuNode, type ContextMenuContextValue } from "@/react/context-menu/context";
78
+ export {
79
+ createContextMenu,
80
+ isAction,
81
+ CONTEXT_MENU_SEARCH_KEYS,
82
+ type ContextMenuEntry,
83
+ type ContextMenuAction,
84
+ type ContextMenuSeparator,
85
+ type ContextMenuLabel,
86
+ type ContextMenuOptions,
87
+ type ContextMenuInstance,
88
+ type ContextMenuState,
89
+ type ContextMenuLevel,
90
+ type ContextMenuPoint,
91
+ type ContextMenuMoveKey
92
+ } from "@/core/context-menu";
93
+ // The clipboard rows as their own surface: a menu built from the parts can add them
94
+ // itself, and a caller that performs Copy in its own way has the same reader and writer.
95
+ export {
96
+ CLIPBOARD_PREFIX,
97
+ clipboardEntries,
98
+ clipboardAction,
99
+ clipboardHasText,
100
+ inspectClipboardTarget,
101
+ performClipboardAction,
102
+ type ClipboardAction,
103
+ type ClipboardTarget,
104
+ type ClipboardMenuOptions,
105
+ type ClipboardMenuLabels
106
+ } from "@/core/clipboard-menu";
107
+ export { CONTEXT_MENU_STYLES } from "@/react/context-menu/styles";
108
+ export { shortcutTokens, shortcutText, parseShortcut, matchesShortcut, type Shortcut, type ShortcutSpec } from "@/core/keys";