@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.
- package/dist/{chunk-QQFNAKMY.js → chunk-45UHLZYT.js} +1 -1
- package/dist/chunk-4VUHQFAT.js +130 -0
- package/dist/{chunk-D5A2ZMAG.js → chunk-4ZPMP47J.js} +6 -1
- package/dist/chunk-DZMHY3SU.js +777 -0
- package/dist/{chunk-FWVWX67R.js → chunk-FGBIZDV2.js} +3 -3
- package/dist/chunk-N6PDHMAX.js +213 -0
- package/dist/chunk-NONGREXC.js +248 -0
- package/dist/{chunk-R4ZAEE7V.js → chunk-QJJ34E4G.js} +34 -10
- package/dist/chunk-S2FPN5ID.js +115 -0
- package/dist/chunk-S7EE57YB.js +9 -0
- package/dist/chunk-VKL3DEIQ.js +804 -0
- package/dist/chunk-WSQC3PCC.js +466 -0
- package/dist/chunk-Z3VDE7OA.js +532 -0
- package/dist/clipboard-menu-B_ouitfS.d.ts +264 -0
- package/dist/color-D_rZ83Oc.d.ts +152 -0
- package/dist/color-OPV3BJV6.js +452 -0
- package/dist/{index-BHpOZncw.d.ts → index-ZPvlf9vs.d.ts} +53 -5
- package/dist/index.d.ts +6 -2
- package/dist/index.js +9 -4
- package/dist/keys-D2zJs1uB.d.ts +100 -0
- package/dist/next/index.d.ts +10 -3
- package/dist/next/index.js +22 -13
- package/dist/react/context-menu.d.ts +219 -0
- package/dist/react/context-menu.js +6 -0
- package/dist/react/index.d.ts +12 -5
- package/dist/react/index.js +21 -12
- package/dist/react/input.d.ts +3 -3
- package/dist/react/input.js +2 -1
- package/dist/react/palette.d.ts +1 -1
- package/dist/react/palette.js +3 -3
- package/dist/react/search.d.ts +1 -1
- package/dist/react/search.js +2 -2
- package/dist/react/select.d.ts +217 -0
- package/dist/react/select.js +7 -0
- package/dist/react/selection.d.ts +104 -0
- package/dist/react/selection.js +4 -0
- package/dist/react-router/index.d.ts +10 -3
- package/dist/react-router/index.js +22 -13
- package/dist/search/index.d.ts +2 -2
- package/dist/search/index.js +1 -1
- package/dist/{search-BD9-5O5U.d.ts → search-DYgqRp37.d.ts} +13 -1
- package/dist/{search-UQEXAPQB.js → search-PBZORZ7P.js} +1 -1
- package/dist/select-ClSy-J1f.d.ts +100 -0
- package/dist/selection-B_pmzHpy.d.ts +150 -0
- package/package.json +23 -2
- package/recipes/color/styles.css +143 -0
- package/recipes/context-menu/styles.css +185 -0
- package/recipes/input/styles.css +9 -0
- package/recipes/palette/styles.css +3 -0
- package/recipes/search/tailwind.tsx +3 -2
- package/recipes/select/styles.css +230 -0
- package/recipes/toast/styles.css +1 -1
- package/registry.json +432 -5
- package/src/core/clipboard-menu.ts +270 -0
- package/src/core/color.ts +248 -0
- package/src/core/context-menu.ts +694 -0
- package/src/core/keys.ts +264 -0
- package/src/core/search.ts +19 -0
- package/src/core/select.ts +404 -0
- package/src/core/selection.ts +648 -0
- package/src/index.ts +92 -1
- package/src/react/context-menu/context.ts +64 -0
- package/src/react/context-menu/index.tsx +108 -0
- package/src/react/context-menu/root.tsx +970 -0
- package/src/react/context-menu/styles.ts +194 -0
- package/src/react/index.ts +100 -1
- package/src/react/input/color-styles.ts +156 -0
- package/src/react/input/color.tsx +466 -0
- package/src/react/input/index.tsx +54 -6
- package/src/react/input/types.ts +59 -3
- package/src/react/palette/root.tsx +2 -2
- package/src/react/select/context.ts +56 -0
- package/src/react/select/index.tsx +96 -0
- package/src/react/select/root.tsx +839 -0
- package/src/react/select/styles.ts +238 -0
- package/src/react/selection/index.tsx +115 -0
- package/src/react/selection/use-selection.ts +260 -0
- package/dist/password-C8lG4Zm9.d.ts +0 -71
- /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";
|