@enigmax/primitives 0.23.0 → 0.25.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-KEVZ5XQV.js → chunk-3MGBZOAU.js} +50 -9
- package/dist/{chunk-DSBYVA7V.js → chunk-DZMHY3SU.js} +73 -9
- package/dist/chunk-KVLSOQTI.js +66 -0
- package/dist/chunk-S2FPN5ID.js +115 -0
- package/dist/{chunk-3BQVOOAM.js → chunk-Z3VDE7OA.js} +145 -1
- package/dist/{context-menu-D3FtTn7v.d.ts → clipboard-menu-B_ouitfS.d.ts} +91 -1
- package/dist/color-D_rZ83Oc.d.ts +152 -0
- package/dist/color-T63FLJNH.js +515 -0
- package/dist/{index-DQNnohoo.d.ts → index-qXOkYQCU.d.ts} +56 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -1
- package/dist/next/index.d.ts +3 -3
- package/dist/next/index.js +6 -4
- package/dist/react/context-menu.d.ts +23 -6
- package/dist/react/context-menu.js +2 -2
- package/dist/react/index.d.ts +5 -5
- package/dist/react/index.js +6 -4
- package/dist/react/input.d.ts +2 -2
- package/dist/react/input.js +2 -1
- package/dist/react-router/index.d.ts +3 -3
- package/dist/react-router/index.js +6 -4
- package/package.json +3 -1
- package/recipes/color/styles.css +163 -0
- package/recipes/context-menu/styles.css +8 -0
- package/registry.json +86 -7
- package/src/core/clipboard-menu.ts +270 -0
- package/src/core/color.ts +248 -0
- package/src/index.ts +32 -0
- package/src/react/context-menu/context.ts +9 -2
- package/src/react/context-menu/index.tsx +14 -0
- package/src/react/context-menu/root.tsx +132 -8
- package/src/react/context-menu/styles.ts +8 -0
- package/src/react/index.ts +32 -0
- package/src/react/input/color-styles.ts +176 -0
- package/src/react/input/color-swatch.tsx +96 -0
- package/src/react/input/color.tsx +527 -0
- package/src/react/input/index.tsx +77 -6
- package/src/react/input/types.ts +63 -3
- package/dist/password-C8lG4Zm9.d.ts +0 -71
- /package/dist/{chunk-3HDEZ2E7.js → chunk-MSOCCQGH.js} +0 -0
|
@@ -171,4 +171,94 @@ interface ContextMenuInstance {
|
|
|
171
171
|
type ContextMenuMoveKey = "ArrowDown" | "ArrowUp" | "Home" | "End" | "PageDown" | "PageUp";
|
|
172
172
|
declare function createContextMenu(options?: ContextMenuOptions): ContextMenuInstance;
|
|
173
173
|
|
|
174
|
-
|
|
174
|
+
/**
|
|
175
|
+
* The rows every context menu on a desktop already has: Copy, Cut and Paste.
|
|
176
|
+
*
|
|
177
|
+
* A menu opened over a selection or over a field is expected to offer them - the browser's own
|
|
178
|
+
* does, and replacing that menu with a custom one takes them away without saying so. Which is
|
|
179
|
+
* why they are ON by default here and turned off with a prop, rather than the other way round:
|
|
180
|
+
* the defect is silent, so the default has to be the safe one.
|
|
181
|
+
*
|
|
182
|
+
* WHAT DECIDES THEY APPEAR is what was right-clicked, and it is read at the moment the menu
|
|
183
|
+
* opens, not from React state - by then the selection is settled (a `contextmenu` event fires
|
|
184
|
+
* after the press has adjusted it) and the caret is still where the visitor left it.
|
|
185
|
+
*
|
|
186
|
+
* - **Copy**, when there is selected text under the pointer.
|
|
187
|
+
* - **Cut**, when that selection is also in something writable.
|
|
188
|
+
* - **Paste**, in anything writable - and disabled when the clipboard is known to be empty.
|
|
189
|
+
*
|
|
190
|
+
* Not a React module: what a selection is, whether a field takes writes, and how text is put
|
|
191
|
+
* back into one are DOM questions, so a menu drawn by anything else gets the same rows.
|
|
192
|
+
*/
|
|
193
|
+
|
|
194
|
+
/** The ids these rows carry. Namespaced, so they can never collide with a caller's own. */
|
|
195
|
+
declare const CLIPBOARD_PREFIX = "enigma:clipboard:";
|
|
196
|
+
type ClipboardAction = "copy" | "cut" | "paste";
|
|
197
|
+
/** What the menu was opened over, as far as the clipboard is concerned. */
|
|
198
|
+
interface ClipboardTarget {
|
|
199
|
+
/** The field or contenteditable under the pointer, or null when it is neither. */
|
|
200
|
+
editable: HTMLElement | null;
|
|
201
|
+
/** Whether that element takes writes: not disabled, not read-only. */
|
|
202
|
+
writable: boolean;
|
|
203
|
+
/** The selected text, from the field's own selection or the document's. */
|
|
204
|
+
selection: string;
|
|
205
|
+
/**
|
|
206
|
+
* Whether that text may be put on the clipboard.
|
|
207
|
+
*
|
|
208
|
+
* False for a password field. The clipboard is shared with every other application on the
|
|
209
|
+
* machine and is not cleared, so a menu that copies a password out of a masked field
|
|
210
|
+
* leaks it somewhere the visitor cannot see - and the browser's own menu refuses too.
|
|
211
|
+
*/
|
|
212
|
+
copyable: boolean;
|
|
213
|
+
/** Where the selection was, so it can be put back after the menu has taken focus. */
|
|
214
|
+
range: {
|
|
215
|
+
start: number;
|
|
216
|
+
end: number;
|
|
217
|
+
} | null;
|
|
218
|
+
}
|
|
219
|
+
interface ClipboardMenuLabels {
|
|
220
|
+
copy?: string;
|
|
221
|
+
cut?: string;
|
|
222
|
+
paste?: string;
|
|
223
|
+
}
|
|
224
|
+
interface ClipboardMenuOptions {
|
|
225
|
+
copy?: boolean;
|
|
226
|
+
cut?: boolean;
|
|
227
|
+
paste?: boolean;
|
|
228
|
+
labels?: ClipboardMenuLabels;
|
|
229
|
+
/** Whatever the renderer draws icons with. `unknown`, because a core cannot know. */
|
|
230
|
+
icons?: {
|
|
231
|
+
copy?: unknown;
|
|
232
|
+
cut?: unknown;
|
|
233
|
+
paste?: unknown;
|
|
234
|
+
};
|
|
235
|
+
/** The clipboard is known to be empty, so Paste is listed and greyed rather than missing. */
|
|
236
|
+
clipboardEmpty?: boolean;
|
|
237
|
+
}
|
|
238
|
+
/** What was right-clicked, read the moment the menu opens. */
|
|
239
|
+
declare function inspectClipboardTarget(node: EventTarget | null): ClipboardTarget;
|
|
240
|
+
/**
|
|
241
|
+
* Whether the clipboard has text in it, or null when that cannot be known.
|
|
242
|
+
*
|
|
243
|
+
* Null is the common answer and not a failure: reading the clipboard needs permission, and
|
|
244
|
+
* asking for it puts a browser prompt on screen just to decide whether to grey out a row -
|
|
245
|
+
* which is a worse trade than showing an enabled Paste that turns out to do nothing. So the
|
|
246
|
+
* permission is only READ, never requested, and the clipboard is only opened where it has
|
|
247
|
+
* already been granted.
|
|
248
|
+
*/
|
|
249
|
+
declare function clipboardHasText(): Promise<boolean | null>;
|
|
250
|
+
/** The rows for this target, in the order every desktop menu puts them. */
|
|
251
|
+
declare function clipboardEntries(target: ClipboardTarget, options?: ClipboardMenuOptions): ContextMenuEntry[];
|
|
252
|
+
/** Which clipboard row an id belongs to, or null for anything that is not one of ours. */
|
|
253
|
+
declare function clipboardAction(id: string): ClipboardAction | null;
|
|
254
|
+
/**
|
|
255
|
+
* Do what the row says.
|
|
256
|
+
*
|
|
257
|
+
* Called straight from the press that chose it, so the browser still counts it as a user
|
|
258
|
+
* gesture - which is what the clipboard API requires and what makes a paste possible at all.
|
|
259
|
+
* Returns whether the action happened: a refused permission and an empty clipboard are both
|
|
260
|
+
* "no", and neither is worth an exception the caller has to catch.
|
|
261
|
+
*/
|
|
262
|
+
declare function performClipboardAction(action: ClipboardAction, target: ClipboardTarget): Promise<boolean>;
|
|
263
|
+
|
|
264
|
+
export { CLIPBOARD_PREFIX as C, CONTEXT_MENU_SEARCH_KEYS as a, type ClipboardAction as b, type ClipboardMenuLabels as c, type ClipboardMenuOptions as d, type ClipboardTarget as e, type ContextMenuAction as f, type ContextMenuEntry as g, type ContextMenuInstance as h, type ContextMenuLabel as i, type ContextMenuLevel as j, type ContextMenuMoveKey as k, type ContextMenuOptions as l, type ContextMenuPoint as m, type ContextMenuSeparator as n, type ContextMenuState as o, clipboardAction as p, clipboardEntries as q, clipboardHasText as r, createContextMenu as s, inspectClipboardTarget as t, isAction as u, performClipboardAction as v };
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Making a password, and judging one.
|
|
3
|
+
*
|
|
4
|
+
* Both are opt-in. A sign-in form wants neither: offering to generate a password where one
|
|
5
|
+
* already exists is noise, and scoring one the visitor cannot change is worse. They belong
|
|
6
|
+
* on a registration form and a change-password form, which is where an agent should switch
|
|
7
|
+
* them on.
|
|
8
|
+
*/
|
|
9
|
+
/** Character classes a generated password can draw from. */
|
|
10
|
+
interface PasswordAlphabet {
|
|
11
|
+
lowercase?: boolean;
|
|
12
|
+
uppercase?: boolean;
|
|
13
|
+
digits?: boolean;
|
|
14
|
+
symbols?: boolean;
|
|
15
|
+
}
|
|
16
|
+
interface GeneratePasswordOptions extends PasswordAlphabet {
|
|
17
|
+
/** Default 20. Long beats clever: length is the only term that scales. */
|
|
18
|
+
length?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Drop the characters that are read wrong off a screen or off paper - I l 1 O 0.
|
|
21
|
+
* Worth it when the password will be typed by hand, not worth the entropy otherwise.
|
|
22
|
+
*/
|
|
23
|
+
excludeAmbiguous?: boolean;
|
|
24
|
+
/** Characters to remove from every class, e.g. ones your backend rejects. */
|
|
25
|
+
exclude?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Guarantee at least one character from every class asked for. Most password policies
|
|
28
|
+
* demand it; it costs a little entropy, because it removes every password that happens
|
|
29
|
+
* to lack one.
|
|
30
|
+
*/
|
|
31
|
+
requireEachClass?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* A random password from the classes asked for.
|
|
35
|
+
*
|
|
36
|
+
* @throws when the runtime has no CSPRNG, or when the options ask for something impossible
|
|
37
|
+
* (every class excluded, or a length too short to hold one of each).
|
|
38
|
+
*/
|
|
39
|
+
declare function generatePassword(options?: GeneratePasswordOptions): string;
|
|
40
|
+
type PasswordScore = 0 | 1 | 2 | 3 | 4;
|
|
41
|
+
interface PasswordStrengthReport {
|
|
42
|
+
/** 0 worst, 4 best. What the bars under the field render. */
|
|
43
|
+
score: PasswordScore;
|
|
44
|
+
/** Estimated bits of entropy after the penalties below. */
|
|
45
|
+
bits: number;
|
|
46
|
+
/**
|
|
47
|
+
* Why it scored what it scored, worst first. Show the first one; showing all of them
|
|
48
|
+
* turns a hint into a lecture.
|
|
49
|
+
*/
|
|
50
|
+
warnings: string[];
|
|
51
|
+
/** Empty field. Render nothing rather than a zero score, which reads as a failure. */
|
|
52
|
+
empty: boolean;
|
|
53
|
+
}
|
|
54
|
+
interface EstimateOptions {
|
|
55
|
+
/**
|
|
56
|
+
* Values the visitor has already typed elsewhere - email, name, company. A password
|
|
57
|
+
* containing one of them is guessable by anyone who has the sign-up form in front of
|
|
58
|
+
* them, and no character-class rule catches it.
|
|
59
|
+
*/
|
|
60
|
+
userInputs?: string[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Score a password.
|
|
64
|
+
*
|
|
65
|
+
* The bits are an estimate and the bands are a convention, not a measurement - they exist
|
|
66
|
+
* to move a bar, not to certify anything. Swap this out for zxcvbn where the number has to
|
|
67
|
+
* mean something, and check the breach corpus for the cases no estimator can see.
|
|
68
|
+
*/
|
|
69
|
+
declare function estimatePasswordStrength(password: string, options?: EstimateOptions): PasswordStrengthReport;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Reading a colour, writing one, and converting between the two models a picker needs.
|
|
73
|
+
*
|
|
74
|
+
* Arithmetic only, like every other core here: no DOM, no framework. The picker drags a
|
|
75
|
+
* saturation/value square and a hue rail, which are HSV; a form field holds `#3b82f6`,
|
|
76
|
+
* `rgb(59, 130, 246)` or an `hsl()`, which is what a stylesheet and a database take. This
|
|
77
|
+
* module is the translation between them.
|
|
78
|
+
*
|
|
79
|
+
* Two things it does NOT do, both on purpose:
|
|
80
|
+
*
|
|
81
|
+
* - **Named colours.** `red`, `rebeccapurple` and the other 146 are a table nobody needs in
|
|
82
|
+
* a bundle to drag a square, and the DOM already resolves them for free (assign the name
|
|
83
|
+
* to `style.color` and read it back). The picker's canonical value is a hex string.
|
|
84
|
+
* - **Colour spaces past sRGB.** `oklch()` and `color()` describe colours a hex cannot, so
|
|
85
|
+
* accepting one here and handing back `#rrggbb` would silently clip it. Parsing returns
|
|
86
|
+
* null instead, which the field reports as unparseable rather than as a different colour.
|
|
87
|
+
*/
|
|
88
|
+
/** sRGB, 0-255 per channel, with alpha 0-1. The transport shape everything converts through. */
|
|
89
|
+
interface Rgb {
|
|
90
|
+
r: number;
|
|
91
|
+
g: number;
|
|
92
|
+
b: number;
|
|
93
|
+
a: number;
|
|
94
|
+
}
|
|
95
|
+
/** Hue 0-360, saturation and value 0-1, alpha 0-1. What the picker's two controls move. */
|
|
96
|
+
interface Hsv {
|
|
97
|
+
h: number;
|
|
98
|
+
s: number;
|
|
99
|
+
v: number;
|
|
100
|
+
a: number;
|
|
101
|
+
}
|
|
102
|
+
/** Hue 0-360, saturation and lightness 0-1, alpha 0-1. Only used by `hsl()` in and out. */
|
|
103
|
+
interface Hsl {
|
|
104
|
+
h: number;
|
|
105
|
+
s: number;
|
|
106
|
+
l: number;
|
|
107
|
+
a: number;
|
|
108
|
+
}
|
|
109
|
+
/** How a colour is written back into the field. */
|
|
110
|
+
type ColorFormat = "hex" | "rgb" | "hsl";
|
|
111
|
+
interface FormatColorOptions {
|
|
112
|
+
/** Write the alpha channel. Off drops it, so a half-transparent colour becomes opaque. */
|
|
113
|
+
alpha?: boolean;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* A colour string to sRGB, or null when it is not one.
|
|
117
|
+
*
|
|
118
|
+
* Null is the whole point of the return type: a field is unparseable for as long as someone
|
|
119
|
+
* is halfway through typing `#3b8`, and a picker that guesses at that moment fights the
|
|
120
|
+
* caret. Everything here is tolerant of what a person types - a missing `#`, upper case,
|
|
121
|
+
* stray spaces - and intolerant of what would be a guess.
|
|
122
|
+
*/
|
|
123
|
+
declare function parseColor(input: string): Rgb | null;
|
|
124
|
+
/** `#rrggbb`, or `#rrggbbaa` when alpha is asked for and the colour is not opaque. */
|
|
125
|
+
declare function toHex(color: Rgb, options?: FormatColorOptions): string;
|
|
126
|
+
/**
|
|
127
|
+
* A colour back to a string.
|
|
128
|
+
*
|
|
129
|
+
* The legacy comma syntax for `rgb()` and `hsl()`, deliberately: this string is going into a
|
|
130
|
+
* field someone will paste into a stylesheet, a spreadsheet or an older toolchain, and the
|
|
131
|
+
* space-and-slash form is the one those still refuse.
|
|
132
|
+
*/
|
|
133
|
+
declare function formatColor(color: Rgb, format?: ColorFormat, options?: FormatColorOptions): string;
|
|
134
|
+
/**
|
|
135
|
+
* sRGB to HSV, keeping a hue the arithmetic cannot see.
|
|
136
|
+
*
|
|
137
|
+
* THE colour picker bug, and it is in almost every hand-rolled one: hue is undefined at
|
|
138
|
+
* black, at white and at every grey, because those have no dominant channel. Recomputing it
|
|
139
|
+
* from the RGB therefore returns 0 - red - so dragging the square into its bottom-left
|
|
140
|
+
* corner and back out again resets a hue the visitor picked, and the rail jumps under their
|
|
141
|
+
* finger. `fallbackHue` is the hue they last chose, and it is what the picker keeps its own
|
|
142
|
+
* state for.
|
|
143
|
+
*/
|
|
144
|
+
declare function rgbToHsv(color: Rgb, fallbackHue?: number): Hsv;
|
|
145
|
+
declare function hsvToRgb(color: Hsv): Rgb;
|
|
146
|
+
/** sRGB to HSL. Same undefined hue at the greys, same reason to pass the one being kept. */
|
|
147
|
+
declare function rgbToHsl(color: Rgb, fallbackHue?: number): Hsl;
|
|
148
|
+
declare function hslToRgb(color: Hsl): Rgb;
|
|
149
|
+
/** Same colour, to the byte. Used to tell a value the picker wrote from one someone typed. */
|
|
150
|
+
declare function colorEquals(a: Rgb | null, b: Rgb | null): boolean;
|
|
151
|
+
|
|
152
|
+
export { type ColorFormat as C, type EstimateOptions as E, type FormatColorOptions as F, type GeneratePasswordOptions as G, type Hsl as H, type PasswordAlphabet as P, type Rgb as R, type Hsv as a, type PasswordScore as b, type PasswordStrengthReport as c, colorEquals as d, estimatePasswordStrength as e, formatColor as f, generatePassword as g, hslToRgb as h, hsvToRgb as i, rgbToHsv as j, parseColor as p, rgbToHsl as r, toHex as t };
|