neba 1.2.0 → 1.3.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/README.md +7 -5
- package/dist/components/alert/Alert.d.ts +7 -1
- package/dist/components/alert/Alert.js +1 -1
- package/dist/components/animate-appear/AnimateAppear.d.ts +55 -0
- package/dist/components/animate-appear/AnimateAppear.js +1 -0
- package/dist/components/animate-appear/index.d.ts +2 -0
- package/dist/components/animate-appear/index.js +1 -0
- package/dist/components/animate-blink/AnimateBlink.d.ts +31 -0
- package/dist/components/animate-blink/AnimateBlink.js +1 -0
- package/dist/components/animate-blink/index.d.ts +2 -0
- package/dist/components/animate-blink/index.js +1 -0
- package/dist/components/animate-fade/AnimateFade.d.ts +31 -0
- package/dist/components/animate-fade/AnimateFade.js +1 -0
- package/dist/components/animate-fade/index.d.ts +2 -0
- package/dist/components/animate-fade/index.js +1 -0
- package/dist/components/animate-grow/AnimateGrow.d.ts +41 -0
- package/dist/components/animate-grow/AnimateGrow.js +1 -0
- package/dist/components/animate-grow/index.d.ts +2 -0
- package/dist/components/animate-grow/index.js +1 -0
- package/dist/components/animate-headline/AnimateHeadline.d.ts +55 -0
- package/dist/components/animate-headline/AnimateHeadline.js +1 -0
- package/dist/components/animate-headline/index.d.ts +2 -0
- package/dist/components/animate-headline/index.js +1 -0
- package/dist/components/animate-lighting/AnimateLighting.d.ts +62 -0
- package/dist/components/animate-lighting/AnimateLighting.js +1 -0
- package/dist/components/animate-lighting/index.d.ts +2 -0
- package/dist/components/animate-lighting/index.js +1 -0
- package/dist/components/animate-marquee/AnimateMarquee.d.ts +65 -0
- package/dist/components/animate-marquee/AnimateMarquee.js +1 -0
- package/dist/components/animate-marquee/index.d.ts +2 -0
- package/dist/components/animate-marquee/index.js +1 -0
- package/dist/components/animate-rotate/AnimateRotate.d.ts +48 -0
- package/dist/components/animate-rotate/AnimateRotate.js +1 -0
- package/dist/components/animate-rotate/index.d.ts +2 -0
- package/dist/components/animate-rotate/index.js +1 -0
- package/dist/components/animate-slide/AnimateSlide.d.ts +46 -0
- package/dist/components/animate-slide/AnimateSlide.js +1 -0
- package/dist/components/animate-slide/index.d.ts +2 -0
- package/dist/components/animate-slide/index.js +1 -0
- package/dist/components/animate-typing/AnimateTyping.d.ts +58 -0
- package/dist/components/animate-typing/AnimateTyping.js +1 -0
- package/dist/components/animate-typing/index.d.ts +2 -0
- package/dist/components/animate-typing/index.js +1 -0
- package/dist/components/animate-zoom/AnimateZoom.d.ts +37 -0
- package/dist/components/animate-zoom/AnimateZoom.js +1 -0
- package/dist/components/animate-zoom/index.d.ts +2 -0
- package/dist/components/animate-zoom/index.js +1 -0
- package/dist/components/aspect-ratio/AspectRatio.d.ts +63 -0
- package/dist/components/aspect-ratio/AspectRatio.js +1 -0
- package/dist/components/aspect-ratio/index.d.ts +2 -0
- package/dist/components/aspect-ratio/index.js +1 -0
- package/dist/components/avatar/Avatar.d.ts +7 -1
- package/dist/components/avatar/Avatar.js +1 -1
- package/dist/components/blockquote/Blockquote.d.ts +7 -1
- package/dist/components/blockquote/Blockquote.js +1 -1
- package/dist/components/box/Box.d.ts +7 -1
- package/dist/components/box/Box.js +1 -1
- package/dist/components/chip/Chip.d.ts +7 -1
- package/dist/components/chip/Chip.js +1 -1
- package/dist/components/color-picker/ColorPicker.d.ts +102 -0
- package/dist/components/color-picker/ColorPicker.js +1 -0
- package/dist/components/color-picker/index.d.ts +2 -0
- package/dist/components/color-picker/index.js +1 -0
- package/dist/components/drawer/Drawer.d.ts +145 -0
- package/dist/components/drawer/Drawer.js +1 -0
- package/dist/components/drawer/index.d.ts +2 -0
- package/dist/components/drawer/index.js +1 -0
- package/dist/components/icon/Icon.d.ts +8 -1
- package/dist/components/icon/Icon.js +1 -1
- package/dist/components/popover/Popover.d.ts +109 -0
- package/dist/components/popover/Popover.js +1 -0
- package/dist/components/popover/index.d.ts +2 -0
- package/dist/components/popover/index.js +1 -0
- package/dist/components/skeleton/Skeleton.d.ts +76 -0
- package/dist/components/skeleton/Skeleton.js +1 -0
- package/dist/components/skeleton/index.d.ts +2 -0
- package/dist/components/skeleton/index.js +1 -0
- package/dist/components/typography/Typography.d.ts +7 -1
- package/dist/components/typography/Typography.js +1 -1
- package/dist/index.d.ts +16 -0
- package/dist/index.js +1 -1
- package/dist/internal/animate.d.ts +175 -0
- package/dist/internal/animate.js +1 -0
- package/dist/internal/color.d.ts +108 -0
- package/dist/internal/color.js +1 -0
- package/dist/internal/i18n.d.ts +25 -0
- package/dist/internal/i18n.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/tailwind.css +379 -0
- package/dist/types.d.ts +112 -0
- package/package.json +8 -8
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The machinery every `Animate*` component runs on, and the `transition` prop
|
|
3
|
+
* with it.
|
|
4
|
+
*
|
|
5
|
+
* It lives in `internal/` for the reason `button-group.ts` and `menu.ts` do:
|
|
6
|
+
* eleven components need it and none of them should have to import another. The
|
|
7
|
+
* `transition` prop needs the same table from the other side — a Card and an
|
|
8
|
+
* `AnimateFade` must produce the same fade, or the library has two fades.
|
|
9
|
+
*
|
|
10
|
+
* ## The shape of it
|
|
11
|
+
*
|
|
12
|
+
* Every effect is one `@keyframes` in `styles.css` running from a state written
|
|
13
|
+
* entirely in custom properties to the element's natural one. Nothing here
|
|
14
|
+
* generates CSS: it fills `--n-anim-*` slots, and the stylesheet decides what
|
|
15
|
+
* they mean. That is the same split `styleSlots()` makes for colour, for the
|
|
16
|
+
* same reason — Tailwind only ever sees class names that appear literally in
|
|
17
|
+
* the source, so a per-value class would not scale past the first prop.
|
|
18
|
+
*
|
|
19
|
+
* Because the from-state is the *keyframe* rather than a second class, running
|
|
20
|
+
* an effect backwards is `animation-direction: reverse` and nothing else. That
|
|
21
|
+
* is what makes `mode="out"` free on all six.
|
|
22
|
+
*
|
|
23
|
+
* ## What is deliberately not here
|
|
24
|
+
*
|
|
25
|
+
* An animation that has to know what its children *are* — a marquee that
|
|
26
|
+
* duplicates them, a headline that swaps between them, a typewriter that
|
|
27
|
+
* counts characters — cannot be a class name and a few numbers. Those are
|
|
28
|
+
* components, and their logic stays in their own files.
|
|
29
|
+
*/
|
|
30
|
+
import * as React from 'react';
|
|
31
|
+
import type { NebaAnimateRepeat, NebaAnimateTrigger, NebaAnimation, NebaSide, NebaTransition } from '../types';
|
|
32
|
+
/**
|
|
33
|
+
* Which keyframe an effect runs.
|
|
34
|
+
*
|
|
35
|
+
* `grow` and `zoom` share one: they are the same arithmetic at two strengths,
|
|
36
|
+
* and a second identical `@keyframes` would only be a second place to fix a
|
|
37
|
+
* bug. What separates them is their defaults and their origin, which is a
|
|
38
|
+
* property rather than a keyframe.
|
|
39
|
+
*/
|
|
40
|
+
export declare const animationClasses: Record<NebaAnimation, string>;
|
|
41
|
+
/** The class that reads the slots. Always paired with one of the above. */
|
|
42
|
+
export declare const animBaseClass = "neba-anim";
|
|
43
|
+
/**
|
|
44
|
+
* How long each effect takes when nobody said.
|
|
45
|
+
*
|
|
46
|
+
* They are not one number because they do not travel the same distance. A fade
|
|
47
|
+
* crosses one axis of opacity; a rotate crosses half a turn and reads as rushed
|
|
48
|
+
* at the same duration. `blink` is the odd one out — it is a cycle rather than
|
|
49
|
+
* an arrival, so its number is a period.
|
|
50
|
+
*/
|
|
51
|
+
export declare const defaultDurations: Record<NebaAnimation, number>;
|
|
52
|
+
/** A number is pixels; a string is already a CSS length. */
|
|
53
|
+
export declare function lengthValue(value: number | string): string;
|
|
54
|
+
export declare function isInfinite(repeat: NebaAnimateRepeat | undefined): boolean;
|
|
55
|
+
export interface AnimationSlotOptions {
|
|
56
|
+
duration: number;
|
|
57
|
+
delay: number;
|
|
58
|
+
easing?: string;
|
|
59
|
+
repeat: NebaAnimateRepeat;
|
|
60
|
+
alternate?: boolean;
|
|
61
|
+
mode?: 'in' | 'out';
|
|
62
|
+
/** Where the animated properties start. Only the ones an effect reads. */
|
|
63
|
+
opacity?: number;
|
|
64
|
+
scale?: number;
|
|
65
|
+
x?: string;
|
|
66
|
+
y?: string;
|
|
67
|
+
angle?: string;
|
|
68
|
+
angleTo?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The `--n-anim-*` slots, as an inline style object.
|
|
72
|
+
*
|
|
73
|
+
* Inline rather than utilities for the reason the colour slots are: these are
|
|
74
|
+
* per-instance numbers, and Tailwind cannot generate a class for a duration it
|
|
75
|
+
* has never seen written down.
|
|
76
|
+
*/
|
|
77
|
+
export declare function animationSlots(options: AnimationSlotOptions): React.CSSProperties;
|
|
78
|
+
/**
|
|
79
|
+
* Which way a slide starts, given the edge it comes from.
|
|
80
|
+
*
|
|
81
|
+
* `NebaSide` is physical everywhere in the library, and it stays physical here:
|
|
82
|
+
* something sliding in from the top of the window comes from the top in every
|
|
83
|
+
* writing direction.
|
|
84
|
+
*/
|
|
85
|
+
export declare function slideOffsets(from: NebaSide, distance: number | string): {
|
|
86
|
+
x: string;
|
|
87
|
+
y: string;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* What a component's `transition` prop turns into: one class pair and a handful
|
|
91
|
+
* of custom properties, merged into whatever the component was already writing.
|
|
92
|
+
*
|
|
93
|
+
* No hook, no ref, no observer — which is the whole design. A component that
|
|
94
|
+
* offers `transition` gains four lines and no behaviour it has to own, and a
|
|
95
|
+
* caller who needs a trigger, a replay or a hover reaches for the `Animate*`
|
|
96
|
+
* component that does exactly that.
|
|
97
|
+
*/
|
|
98
|
+
export declare function transitionProps(transition: NebaTransition | undefined): {
|
|
99
|
+
className: string;
|
|
100
|
+
style: React.CSSProperties;
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Whether the reader has asked for less motion.
|
|
104
|
+
*
|
|
105
|
+
* The CSS side of this is handled in the stylesheet, where every keyframe is
|
|
106
|
+
* switched off at once. This is for the three effects whose motion is written
|
|
107
|
+
* in JavaScript — a typewriter, a headline reel, a measured marquee — where
|
|
108
|
+
* there is no rule to switch off and the component has to decide for itself
|
|
109
|
+
* what "still" means.
|
|
110
|
+
*/
|
|
111
|
+
export declare function usePrefersReducedMotion(): boolean;
|
|
112
|
+
export interface AnimationRunOptions {
|
|
113
|
+
trigger: NebaAnimateTrigger;
|
|
114
|
+
play?: boolean;
|
|
115
|
+
once: boolean;
|
|
116
|
+
threshold: number;
|
|
117
|
+
paused?: boolean;
|
|
118
|
+
/** An infinite effect stops when the pointer leaves; a finite one finishes. */
|
|
119
|
+
infinite: boolean;
|
|
120
|
+
}
|
|
121
|
+
export interface AnimationRun {
|
|
122
|
+
/** Goes on the animated element. */
|
|
123
|
+
ref: React.RefCallback<HTMLElement>;
|
|
124
|
+
/** `running` or `paused`, for `--n-anim-state`. */
|
|
125
|
+
state: 'running' | 'paused';
|
|
126
|
+
/** Whether the animation has been let go at all, for `data-state`. */
|
|
127
|
+
started: boolean;
|
|
128
|
+
/** Spread onto the element when `trigger` is `hover`; empty otherwise. */
|
|
129
|
+
handlers: React.HTMLAttributes<HTMLElement>;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Starts, restarts and holds an animation.
|
|
133
|
+
*
|
|
134
|
+
* Two things here are less obvious than they look.
|
|
135
|
+
*
|
|
136
|
+
* **Waiting is `animation-play-state: paused`, not a second class.** An
|
|
137
|
+
* element that has not been triggered yet has to already look like its own
|
|
138
|
+
* first frame, or a `visible` fade would be fully drawn until it scrolled into
|
|
139
|
+
* view and then blink out to start. With `fill-mode: both` a paused animation
|
|
140
|
+
* shows exactly that frame, so waiting and running are one animation in two
|
|
141
|
+
* states rather than two states to keep in step.
|
|
142
|
+
*
|
|
143
|
+
* **Restarting reaches for the DOM.** There is no way to rewind a CSS animation
|
|
144
|
+
* from React: re-rendering with the same class changes nothing, and a `key`
|
|
145
|
+
* would restart the animation by unmounting the children, taking their state
|
|
146
|
+
* with it. Clearing `animation-name`, reading a layout property to force the
|
|
147
|
+
* style to settle, and putting it back is the one move that rewinds the element
|
|
148
|
+
* and leaves everything inside it alone.
|
|
149
|
+
*/
|
|
150
|
+
export declare function useAnimationRun({ trigger, play, once, threshold, paused, infinite }: AnimationRunOptions): AnimationRun;
|
|
151
|
+
export interface AnimateElementParams extends AnimationSlotOptions, AnimationRunOptions {
|
|
152
|
+
/** Which keyframe. `null` for the components that write their own. */
|
|
153
|
+
effect: NebaAnimation | null;
|
|
154
|
+
}
|
|
155
|
+
export interface AnimateElement {
|
|
156
|
+
ref: React.RefCallback<HTMLElement>;
|
|
157
|
+
className: string;
|
|
158
|
+
style: React.CSSProperties;
|
|
159
|
+
/** The hover handlers and the two data attributes, ready to be spread. */
|
|
160
|
+
props: React.HTMLAttributes<HTMLElement> & Record<string, unknown>;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Everything an `Animate*` root needs, in one call.
|
|
164
|
+
*
|
|
165
|
+
* The six effect components differ only in their defaults and in which slots
|
|
166
|
+
* they fill, so this is where the identical two-thirds of each of them lives.
|
|
167
|
+
* The four that have to understand their children — Typing, Marquee, Headline,
|
|
168
|
+
* Appear — call `useAnimationRun` directly and put the classes where their own
|
|
169
|
+
* structure needs them, which is why `effect` is allowed to be `null`.
|
|
170
|
+
*
|
|
171
|
+
* `data-neba-animation` and `data-state` are here rather than in each component
|
|
172
|
+
* because they are the same two facts every time, and because a test that has
|
|
173
|
+
* to assert on a class name is a test that breaks when a class name changes.
|
|
174
|
+
*/
|
|
175
|
+
export declare function useAnimateElement(params: AnimateElementParams): AnimateElement;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import*as e from"react";export const animationClasses={fade:"neba-anim-fade",grow:"neba-anim-scale",slide:"neba-anim-slide",zoom:"neba-anim-scale",rotate:"neba-anim-rotate",blink:"neba-anim-blink"};export const animBaseClass="neba-anim";export const defaultDurations={fade:320,grow:340,slide:380,zoom:340,rotate:460,blink:900};export function lengthValue(e){return"number"==typeof e?`${e}px`:e}export function isInfinite(e){return"infinite"===e}export function animationSlots(e){const n={"--n-anim-duration":`${e.duration}ms`,"--n-anim-delay":`${e.delay}ms`,"--n-anim-repeat":(i=e.repeat,"infinite"===i?"infinite":String(i)),"--n-anim-direction":(t=e.mode??"in",a=e.alternate,"out"===t?a?"alternate-reverse":"reverse":a?"alternate":"normal")};var t,a,i;return e.easing&&(n["--n-anim-ease"]=e.easing),void 0!==e.opacity&&(n["--n-anim-opacity"]=String(e.opacity)),void 0!==e.scale&&(n["--n-anim-scale"]=String(e.scale)),void 0!==e.x&&(n["--n-anim-x"]=e.x),void 0!==e.y&&(n["--n-anim-y"]=e.y),void 0!==e.angle&&(n["--n-anim-angle"]=e.angle),void 0!==e.angleTo&&(n["--n-anim-angle-to"]=e.angleTo),n}export function slideOffsets(e,n){const t=lengthValue(n),a="number"==typeof n?-n+"px":`calc(-1 * ${t})`;switch(e){case"top":return{x:"0px",y:a};case"bottom":return{x:"0px",y:t};case"left":return{x:a,y:"0px"};default:return{x:t,y:"0px"}}}export function transitionProps(e){if(!e)return{className:"",style:{}};const n="string"==typeof e?{type:e}:e,{type:t}=n,a={duration:n.duration??defaultDurations[t],delay:n.delay??0,easing:n.easing,repeat:n.repeat??("blink"===t?"infinite":1),alternate:n.alternate};if("grow"!==t&&"zoom"!==t||(a.scale=n.scale??("zoom"===t?.4:.8)),"slide"===t){const{x:e,y:t}=slideOffsets(n.from??"bottom",n.distance??"100%");a.x=e,a.y=t}return"rotate"===t&&(a.angle=`${n.angle??-180}deg`),{className:`neba-anim ${animationClasses[t]}`,style:animationSlots(a)}}export function usePrefersReducedMotion(){const[n,t]=e.useState(!1);return e.useEffect(()=>{if("undefined"==typeof window||!window.matchMedia)return;const e=window.matchMedia("(prefers-reduced-motion: reduce)");t(e.matches);const n=e=>t(e.matches);return e.addEventListener("change",n),()=>e.removeEventListener("change",n)},[]),n}export function useAnimationRun({trigger:n,play:t,once:a,threshold:i,paused:o,infinite:r}){const s=e.useRef(null),[c,l]=e.useState("mount"===n),[u,m]=e.useState(0),d=e.useCallback(()=>{l(!0),m(e=>e+1)},[]);e.useLayoutEffect(()=>{const e=s.current;if(!e||0===u)return;const n=[e,...e.querySelectorAll(".neba-anim, .neba-marquee-track")];for(const e of n)e.style.animationName="none";e.offsetWidth;for(const e of n)e.style.animationName=""},[u]),e.useEffect(()=>{if("visible"!==n)return;const e=s.current;if(!e||"undefined"==typeof IntersectionObserver)return void l(!0);const t=new IntersectionObserver(([e])=>{e.isIntersecting?(d(),a&&t.disconnect()):a||l(!1)},{threshold:i});return t.observe(e),()=>t.disconnect()},[n,a,i,d]),e.useEffect(()=>{"manual"===n&&(t?d():l(!1))},[n,t,d]);const f="hover"===n?{onPointerEnter:d,onFocus:d,onPointerLeave:()=>{r&&l(!1)},onBlur:()=>{r&&l(!1)}}:{};return{ref:e.useCallback(e=>{s.current=e},[]),state:c&&!o?"running":"paused",started:c,handlers:f}}export function useAnimateElement(e){const{effect:n,trigger:t,play:a,once:i,threshold:o,paused:r,infinite:s,...c}=e,l=useAnimationRun({trigger:t,play:a,once:i,threshold:o,paused:r,infinite:s});return{ref:l.ref,className:n?`neba-anim ${animationClasses[n]}`:"",style:{...animationSlots(c),"--n-anim-state":l.state},props:{...l.handlers,"data-neba-animation":n??void 0,"data-state":l.state}}}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Colour arithmetic, written out rather than installed.
|
|
3
|
+
*
|
|
4
|
+
* ColorPicker is the only component that has to *compute* a colour rather than
|
|
5
|
+
* name one, and the whole of what it needs is here: three representations, the
|
|
6
|
+
* conversions between them, one parser and one formatter. It is a hundred lines
|
|
7
|
+
* of trigonometry-free arithmetic, which is the entire reason the library still
|
|
8
|
+
* has one runtime dependency.
|
|
9
|
+
*
|
|
10
|
+
* Two decisions worth knowing before reading:
|
|
11
|
+
*
|
|
12
|
+
* - **HSV is the model the panel is drawn in, and it never leaves.** A
|
|
13
|
+
* saturation/value square with a hue rail beside it *is* HSV, so the picker
|
|
14
|
+
* keeps its state that way and converts on the way out. Round-tripping
|
|
15
|
+
* through RGB instead would lose the hue of every greyscale colour — black is
|
|
16
|
+
* `#000` at every hue — and the rail would jump to red the moment the pointer
|
|
17
|
+
* reached a corner.
|
|
18
|
+
* - **`alpha` is 0–1, everything else is what CSS says it is.** `h` is degrees,
|
|
19
|
+
* `s`/`v`/`l` are percentages, `r`/`g`/`b` are 0–255. No component invents a
|
|
20
|
+
* normalised unit, because every one of these numbers is eventually written
|
|
21
|
+
* into a string a human reads.
|
|
22
|
+
*
|
|
23
|
+
* Nothing here is exported from `src/index.ts`. It is the library talking to
|
|
24
|
+
* itself, in the sense that `internal/` always means.
|
|
25
|
+
*/
|
|
26
|
+
/** 0–255 per channel, the way a hex triplet and `rgb()` both spell it. */
|
|
27
|
+
export interface Rgb {
|
|
28
|
+
r: number;
|
|
29
|
+
g: number;
|
|
30
|
+
b: number;
|
|
31
|
+
}
|
|
32
|
+
/** `h` in degrees, `s` and `v` as percentages. The panel's own model. */
|
|
33
|
+
export interface Hsv {
|
|
34
|
+
h: number;
|
|
35
|
+
s: number;
|
|
36
|
+
v: number;
|
|
37
|
+
}
|
|
38
|
+
/** `h` in degrees, `s` and `l` as percentages. Only ever an output format. */
|
|
39
|
+
export interface Hsl {
|
|
40
|
+
h: number;
|
|
41
|
+
s: number;
|
|
42
|
+
l: number;
|
|
43
|
+
}
|
|
44
|
+
/** A colour and how much of it there is. `alpha` is 0–1. */
|
|
45
|
+
export interface ColorValue {
|
|
46
|
+
hsv: Hsv;
|
|
47
|
+
alpha: number;
|
|
48
|
+
}
|
|
49
|
+
/** The three ways the picker will write a colour back out. */
|
|
50
|
+
export type ColorFormat = 'hex' | 'rgb' | 'hsl';
|
|
51
|
+
export declare function clamp(value: number, min: number, max: number): number;
|
|
52
|
+
export declare function hsvToRgb({ h, s, v }: Hsv): Rgb;
|
|
53
|
+
export declare function rgbToHsv({ r, g, b }: Rgb): Hsv;
|
|
54
|
+
export declare function rgbToHsl({ r, g, b }: Rgb): Hsl;
|
|
55
|
+
export declare function hslToRgb({ h, s, l }: Hsl): Rgb;
|
|
56
|
+
/**
|
|
57
|
+
* A CSS colour string, or `null` if it is not one this understands.
|
|
58
|
+
*
|
|
59
|
+
* Hex in all four lengths, `rgb()`/`rgba()` and `hsl()`/`hsla()` in both the
|
|
60
|
+
* comma and the space syntax. Named colours and `color()` are deliberately out:
|
|
61
|
+
* a picker has to be able to write every value it can read, and there is no
|
|
62
|
+
* honest way back from `rebeccapurple` to a point on the panel.
|
|
63
|
+
*/
|
|
64
|
+
export declare function parseColor(input: string): ColorValue | null;
|
|
65
|
+
export declare function rgbToHex({ r, g, b }: Rgb, alpha?: number): string;
|
|
66
|
+
/**
|
|
67
|
+
* The value the component hands back, in whichever notation was asked for.
|
|
68
|
+
*
|
|
69
|
+
* `hex` drops the alpha pair when the colour is opaque, and the two functional
|
|
70
|
+
* forms drop the fourth argument for the same reason — a caller who never
|
|
71
|
+
* turned `alpha` on should never see `rgba(…, 1)` come out of a control they
|
|
72
|
+
* only used three channels of.
|
|
73
|
+
*/
|
|
74
|
+
export declare function formatColor(hsv: Hsv, alpha: number, format: ColorFormat): string;
|
|
75
|
+
/** A plain `rgb()` for the swatches and rails, where alpha is drawn separately. */
|
|
76
|
+
export declare function cssColor(hsv: Hsv, alpha?: number): string;
|
|
77
|
+
/**
|
|
78
|
+
* Black or white, whichever can be read on top of this colour.
|
|
79
|
+
*
|
|
80
|
+
* The tick on a chosen swatch is the only thing the picker draws *on* an
|
|
81
|
+
* arbitrary colour, and a fixed white tick disappears on yellow. The threshold
|
|
82
|
+
* is relative luminance rather than plain lightness, because the eye weighs
|
|
83
|
+
* green about six times as heavily as blue and a colour model that pretends
|
|
84
|
+
* otherwise puts the tick the wrong way round on both.
|
|
85
|
+
*/
|
|
86
|
+
export declare function readableInk(hsv: Hsv): string;
|
|
87
|
+
/**
|
|
88
|
+
* The swatches a picker shows when it was given none.
|
|
89
|
+
*
|
|
90
|
+
* Deliberately not the library's own six families: those are semantic roles and
|
|
91
|
+
* a picker is asked for a colour, not for a meaning. This is a plain spectrum
|
|
92
|
+
* plus the greys — the row somebody reaches for when they are labelling a
|
|
93
|
+
* calendar, a tag or a project, which is what a colour picker in an application
|
|
94
|
+
* is nearly always for.
|
|
95
|
+
*/
|
|
96
|
+
export declare const defaultSwatches: readonly string[];
|
|
97
|
+
/**
|
|
98
|
+
* The chequerboard behind a translucent colour, as a background shorthand.
|
|
99
|
+
*
|
|
100
|
+
* Two conic gradients would be one declaration fewer, but a chequer drawn with
|
|
101
|
+
* conic gradients has a seam down the middle of every tile on a fractional
|
|
102
|
+
* device pixel ratio. Four linear stops at 45° do not.
|
|
103
|
+
*/
|
|
104
|
+
export declare const checkerBackground: {
|
|
105
|
+
readonly backgroundImage: "linear-gradient(45deg, var(--neba-border) 25%, transparent 25%), linear-gradient(-45deg, var(--neba-border) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, var(--neba-border) 75%), linear-gradient(-45deg, transparent 75%, var(--neba-border) 75%)";
|
|
106
|
+
readonly backgroundSize: "8px 8px";
|
|
107
|
+
readonly backgroundPosition: "0 0, 0 4px, 4px -4px, -4px 0";
|
|
108
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function clamp(r,n,t){return r<n?n:r>t?t:r}function r(r){const n=r%360;return n<0?n+360:n}function n(r){return Math.round(r)}export function hsvToRgb({h:t,s:e,v:o}){const a=r(t)/60,s=clamp(e,0,100)/100,c=clamp(o,0,100)/100,g=c*s,u=g*(1-Math.abs(a%2-1)),l=c-g,b=Math.floor(a)%6,[i,f,h]=[[g,u,0],[u,g,0],[0,g,u],[0,u,g],[u,0,g],[g,0,u]][b];return{r:n(255*(i+l)),g:n(255*(f+l)),b:n(255*(h+l))}}export function rgbToHsv({r:n,g:t,b:e}){const o=clamp(n,0,255)/255,a=clamp(t,0,255)/255,s=clamp(e,0,255)/255,c=Math.max(o,a,s),g=c-Math.min(o,a,s);let u=0;return 0!==g&&(u=c===o?(a-s)/g%6:c===a?(s-o)/g+2:(o-a)/g+4),{h:r(60*u),s:0===c?0:g/c*100,v:100*c}}export function rgbToHsl({r:r,g:n,b:t}){const{h:e,s:o,v:a}=rgbToHsv({r:r,g:n,b:t}),s=a/100,c=s*(1-o/100/2),g=Math.min(c,1-c);return{h:e,s:0===g?0:(s-c)/g*100,l:100*c}}export function hslToRgb({h:r,s:n,l:t}){const e=clamp(t,0,100)/100,o=e+clamp(n,0,100)/100*Math.min(e,1-e);return hsvToRgb({h:r,s:0===o?0:2*(1-e/o)*100,v:100*o})}const t=/^#?([0-9a-f]{3,8})$/i;export function parseColor(r){const n=r.trim().toLowerCase();if(!n)return null;if(n.startsWith("#")||t.test(n))return function(r){const n=t.exec(r.trim());if(!n)return null;const e=n[1],o=3===e.length||4===e.length?[...e].map(r=>r+r).join(""):e;if(6!==o.length&&8!==o.length)return null;const a=r=>parseInt(o.slice(2*r,2*r+2),16);return{hsv:rgbToHsv({r:a(0),g:a(1),b:a(2)}),alpha:8===o.length?a(3)/255:1}}(n);const e=/^(rgba?|hsla?)\((.*)\)$/.exec(n);if(!e)return null;const[,o,a]=e,s=function(r){return(r.match(/-?[\d.]+%?/g)??[]).map(r=>(r.endsWith("%"),parseFloat(r)))}(a);if(s.length<3||s.some(Number.isNaN))return null;const c=function(r,n,t){if(n.length<=t)return 1;const e=r.match(/-?[\d.]+%?/g)??[];return clamp(e[t]?.endsWith("%")?n[t]/100:n[t],0,1)}(a,s,3);return o.startsWith("rgb")?{hsv:rgbToHsv({r:s[0],g:s[1],b:s[2]}),alpha:c}:{hsv:rgbToHsv(hslToRgb({h:s[0],s:s[1],l:s[2]})),alpha:c}}function e(r){return clamp(n(r),0,255).toString(16).padStart(2,"0")}export function rgbToHex({r:r,g:n,b:t},o=1){const a=`#${e(r)}${e(n)}${e(t)}`;return o>=1?a:`${a}${e(255*o)}`}function o(r){return String(Math.round(100*clamp(r,0,1))/100)}export function formatColor(r,t,e){const a=hsvToRgb(r);if("hex"===e)return rgbToHex(a,t);if("rgb"===e){const r=`${n(a.r)}, ${n(a.g)}, ${n(a.b)}`;return t>=1?`rgb(${r})`:`rgba(${r}, ${o(t)})`}const{h:s,s:c,l:g}=rgbToHsl(a),u=`${n(s)}, ${n(c)}%, ${n(g)}%`;return t>=1?`hsl(${u})`:`hsla(${u}, ${o(t)})`}export function cssColor(r,n=1){const{r:t,g:e,b:a}=hsvToRgb(r);return n>=1?`rgb(${t}, ${e}, ${a})`:`rgba(${t}, ${e}, ${a}, ${o(n)})`}export function readableInk(r){const{r:n,g:t,b:e}=hsvToRgb(r),o=r=>{const n=r/255;return n<=.03928?n/12.92:((n+.055)/1.055)**2.4};return.2126*o(n)+.7152*o(t)+.0722*o(e)>.42?"#000000":"#ffffff"}export const defaultSwatches=["#000000","#4b5563","#9ca3af","#ffffff","#ef4444","#f97316","#f59e0b","#84cc16","#22c55e","#14b8a6","#06b6d4","#3b82f6","#6366f1","#8b5cf6","#d946ef","#ec4899"];export const checkerBackground={backgroundImage:"linear-gradient(45deg, var(--neba-border) 25%, transparent 25%), linear-gradient(-45deg, var(--neba-border) 25%, transparent 25%), linear-gradient(45deg, transparent 75%, var(--neba-border) 75%), linear-gradient(-45deg, transparent 75%, var(--neba-border) 75%)",backgroundSize:"8px 8px",backgroundPosition:"0 0, 0 4px, 4px -4px, -4px 0"};
|
package/dist/internal/i18n.d.ts
CHANGED
|
@@ -62,6 +62,31 @@ export interface NebaMessages {
|
|
|
62
62
|
/** What the three dots mean. */
|
|
63
63
|
typing: string;
|
|
64
64
|
};
|
|
65
|
+
/**
|
|
66
|
+
* ColorPicker.
|
|
67
|
+
*
|
|
68
|
+
* All but one of these are names for something with no text on it: a square
|
|
69
|
+
* of colour, two rails, a grid of swatches. A control a sighted reader
|
|
70
|
+
* understands from a glance at a gradient is a control that says nothing at
|
|
71
|
+
* all without them, which makes this the one namespace here where the strings
|
|
72
|
+
* are the entire accessible interface rather than a convenience on top of it.
|
|
73
|
+
*/
|
|
74
|
+
color: {
|
|
75
|
+
/** The saturation/brightness square. */
|
|
76
|
+
area: string;
|
|
77
|
+
/** The rail beside it. */
|
|
78
|
+
hue: string;
|
|
79
|
+
/** The rail below that, when `alpha` is on. */
|
|
80
|
+
alpha: string;
|
|
81
|
+
/** The field the colour can be typed into. */
|
|
82
|
+
value: string;
|
|
83
|
+
/** The grid of ready-made colours. */
|
|
84
|
+
swatches: string;
|
|
85
|
+
/** The × that empties the control. */
|
|
86
|
+
clear: string;
|
|
87
|
+
/** What the trigger reads before anything has been chosen. */
|
|
88
|
+
empty: string;
|
|
89
|
+
};
|
|
65
90
|
}
|
|
66
91
|
/**
|
|
67
92
|
* The strings for a locale, merged over English.
|
package/dist/internal/i18n.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import*as e from"react";const
|
|
1
|
+
import*as e from"react";const a={link:{newTab:"(opens in a new tab)"},spoiler:{reveal:"Reveal",hide:"Hide",notice:"This may contain spoilers"},chat:{sending:"Sending",sent:"Sent",delivered:"Delivered",read:"Read",failed:"Not sent",typing:"Typing…"},color:{area:"Saturation and brightness",hue:"Hue",alpha:"Opacity",value:"Colour value",swatches:"Preset colours",clear:"Clear",empty:"Choose a colour"}},n={ko:{link:{newTab:"(새 창에서 열림)"},spoiler:{reveal:"내용 보기",hide:"숨기기",notice:"스포일러가 포함되어 있을 수 있습니다"},chat:{sending:"보내는 중",sent:"보냄",delivered:"전달됨",read:"읽음",failed:"전송 실패",typing:"입력 중…"},color:{area:"채도와 명도",hue:"색상",alpha:"불투명도",value:"색상 값",swatches:"기본 색상",clear:"지우기",empty:"색상 선택"}},ja:{link:{newTab:"(新しいタブで開きます)"},spoiler:{reveal:"表示する",hide:"隠す",notice:"ネタバレを含む可能性があります"},chat:{sending:"送信中",sent:"送信済み",delivered:"配信済み",read:"既読",failed:"送信できませんでした",typing:"入力中…"},color:{area:"彩度と明度",hue:"色相",alpha:"不透明度",value:"カラー値",swatches:"プリセットの色",clear:"クリア",empty:"色を選択"}},"zh-hans":{link:{newTab:"(在新标签页中打开)"},spoiler:{reveal:"显示内容",hide:"隐藏",notice:"此内容可能包含剧透"},chat:{sending:"发送中",sent:"已发送",delivered:"已送达",read:"已读",failed:"发送失败",typing:"正在输入…"},color:{area:"饱和度和明度",hue:"色相",alpha:"不透明度",value:"颜色值",swatches:"预设颜色",clear:"清除",empty:"选择颜色"}},"zh-hant":{link:{newTab:"(在新分頁中開啟)"},spoiler:{reveal:"顯示內容",hide:"隱藏",notice:"此內容可能包含劇透"},chat:{sending:"傳送中",sent:"已傳送",delivered:"已送達",read:"已讀",failed:"傳送失敗",typing:"正在輸入…"},color:{area:"飽和度與明度",hue:"色相",alpha:"不透明度",value:"顏色值",swatches:"預設顏色",clear:"清除",empty:"選擇顏色"}},es:{link:{newTab:"(se abre en una pestaña nueva)"},spoiler:{reveal:"Mostrar",hide:"Ocultar",notice:"Puede contener spoilers"},chat:{sending:"Enviando",sent:"Enviado",delivered:"Entregado",read:"Leído",failed:"No enviado",typing:"Escribiendo…"},color:{area:"Saturación y brillo",hue:"Tono",alpha:"Opacidad",value:"Valor del color",swatches:"Colores predefinidos",clear:"Borrar",empty:"Elegir un color"}},pt:{link:{newTab:"(abre em uma nova aba)"},spoiler:{reveal:"Mostrar",hide:"Ocultar",notice:"Pode conter spoilers"},chat:{sending:"Enviando",sent:"Enviado",delivered:"Entregue",read:"Lido",failed:"Não enviado",typing:"Digitando…"},color:{area:"Saturação e brilho",hue:"Matiz",alpha:"Opacidade",value:"Valor da cor",swatches:"Cores predefinidas",clear:"Limpar",empty:"Escolher uma cor"}},fr:{link:{newTab:"(s’ouvre dans un nouvel onglet)"},spoiler:{reveal:"Afficher",hide:"Masquer",notice:"Peut contenir des spoilers"},chat:{sending:"Envoi en cours",sent:"Envoyé",delivered:"Distribué",read:"Lu",failed:"Non envoyé",typing:"En train d’écrire…"},color:{area:"Saturation et luminosité",hue:"Teinte",alpha:"Opacité",value:"Valeur de la couleur",swatches:"Couleurs prédéfinies",clear:"Effacer",empty:"Choisir une couleur"}},de:{link:{newTab:"(wird in einem neuen Tab geöffnet)"},spoiler:{reveal:"Anzeigen",hide:"Ausblenden",notice:"Kann Spoiler enthalten"},chat:{sending:"Wird gesendet",sent:"Gesendet",delivered:"Zugestellt",read:"Gelesen",failed:"Nicht gesendet",typing:"Schreibt…"},color:{area:"Sättigung und Helligkeit",hue:"Farbton",alpha:"Deckkraft",value:"Farbwert",swatches:"Vorgegebene Farben",clear:"Löschen",empty:"Farbe auswählen"}},it:{link:{newTab:"(si apre in una nuova scheda)"},spoiler:{reveal:"Mostra",hide:"Nascondi",notice:"Può contenere spoiler"},chat:{sending:"Invio in corso",sent:"Inviato",delivered:"Consegnato",read:"Letto",failed:"Non inviato",typing:"Sta scrivendo…"},color:{area:"Saturazione e luminosità",hue:"Tonalità",alpha:"Opacità",value:"Valore del colore",swatches:"Colori predefiniti",clear:"Cancella",empty:"Scegli un colore"}},nl:{link:{newTab:"(opent in een nieuw tabblad)"},spoiler:{reveal:"Tonen",hide:"Verbergen",notice:"Kan spoilers bevatten"},chat:{sending:"Wordt verzonden",sent:"Verzonden",delivered:"Afgeleverd",read:"Gelezen",failed:"Niet verzonden",typing:"Aan het typen…"},color:{area:"Verzadiging en helderheid",hue:"Kleurtoon",alpha:"Dekking",value:"Kleurwaarde",swatches:"Vooraf ingestelde kleuren",clear:"Wissen",empty:"Kies een kleur"}},pl:{link:{newTab:"(otwiera się w nowej karcie)"},spoiler:{reveal:"Pokaż",hide:"Ukryj",notice:"Może zawierać spoilery"},chat:{sending:"Wysyłanie",sent:"Wysłano",delivered:"Dostarczono",read:"Przeczytano",failed:"Nie wysłano",typing:"Pisze…"},color:{area:"Nasycenie i jasność",hue:"Barwa",alpha:"Krycie",value:"Wartość koloru",swatches:"Kolory predefiniowane",clear:"Wyczyść",empty:"Wybierz kolor"}},ru:{link:{newTab:"(откроется в новой вкладке)"},spoiler:{reveal:"Показать",hide:"Скрыть",notice:"Может содержать спойлеры"},chat:{sending:"Отправляется",sent:"Отправлено",delivered:"Доставлено",read:"Прочитано",failed:"Не отправлено",typing:"Печатает…"},color:{area:"Насыщенность и яркость",hue:"Оттенок",alpha:"Непрозрачность",value:"Значение цвета",swatches:"Готовые цвета",clear:"Очистить",empty:"Выберите цвет"}},tr:{link:{newTab:"(yeni sekmede açılır)"},spoiler:{reveal:"Göster",hide:"Gizle",notice:"Spoiler içerebilir"},chat:{sending:"Gönderiliyor",sent:"Gönderildi",delivered:"İletildi",read:"Okundu",failed:"Gönderilemedi",typing:"Yazıyor…"},color:{area:"Doygunluk ve parlaklık",hue:"Renk tonu",alpha:"Opaklık",value:"Renk değeri",swatches:"Hazır renkler",clear:"Temizle",empty:"Bir renk seçin"}},ar:{link:{newTab:"(يفتح في علامة تبويب جديدة)"},spoiler:{reveal:"إظهار",hide:"إخفاء",notice:"قد يحتوي على حرق للأحداث"},chat:{sending:"جارٍ الإرسال",sent:"تم الإرسال",delivered:"تم التسليم",read:"تمت القراءة",failed:"لم يتم الإرسال",typing:"يكتب الآن…"},color:{area:"التشبع والسطوع",hue:"درجة اللون",alpha:"العتامة",value:"قيمة اللون",swatches:"ألوان جاهزة",clear:"مسح",empty:"اختر لونًا"}},hi:{link:{newTab:"(नए टैब में खुलता है)"},spoiler:{reveal:"दिखाएँ",hide:"छिपाएँ",notice:"इसमें स्पॉइलर हो सकते हैं"},chat:{sending:"भेजा जा रहा है",sent:"भेजा गया",delivered:"डिलीवर हुआ",read:"पढ़ा गया",failed:"नहीं भेजा गया",typing:"टाइप कर रहे हैं…"},color:{area:"संतृप्ति और चमक",hue:"रंग",alpha:"अपारदर्शिता",value:"रंग मान",swatches:"पूर्व निर्धारित रंग",clear:"साफ़ करें",empty:"एक रंग चुनें"}},id:{link:{newTab:"(terbuka di tab baru)"},spoiler:{reveal:"Tampilkan",hide:"Sembunyikan",notice:"Mungkin mengandung spoiler"},chat:{sending:"Mengirim",sent:"Terkirim",delivered:"Diterima",read:"Dibaca",failed:"Gagal terkirim",typing:"Sedang mengetik…"},color:{area:"Saturasi dan kecerahan",hue:"Rona",alpha:"Opasitas",value:"Nilai warna",swatches:"Warna preset",clear:"Hapus",empty:"Pilih warna"}},vi:{link:{newTab:"(mở trong tab mới)"},spoiler:{reveal:"Hiện nội dung",hide:"Ẩn",notice:"Có thể chứa nội dung tiết lộ"},chat:{sending:"Đang gửi",sent:"Đã gửi",delivered:"Đã nhận",read:"Đã xem",failed:"Chưa gửi được",typing:"Đang nhập…"},color:{area:"Độ bão hòa và độ sáng",hue:"Sắc độ",alpha:"Độ mờ đục",value:"Giá trị màu",swatches:"Màu dựng sẵn",clear:"Xóa",empty:"Chọn màu"}},th:{link:{newTab:"(เปิดในแท็บใหม่)"},spoiler:{reveal:"แสดงเนื้อหา",hide:"ซ่อน",notice:"อาจมีการเปิดเผยเนื้อหา"},chat:{sending:"กำลังส่ง",sent:"ส่งแล้ว",delivered:"ส่งถึงแล้ว",read:"อ่านแล้ว",failed:"ส่งไม่สำเร็จ",typing:"กำลังพิมพ์…"},color:{area:"ความอิ่มตัวและความสว่าง",hue:"เฉดสี",alpha:"ความทึบ",value:"ค่าสี",swatches:"สีที่กำหนดไว้",clear:"ล้าง",empty:"เลือกสี"}}},i={zh:"zh-hans","zh-cn":"zh-hans","zh-my":"zh-hans","zh-sg":"zh-hans","zh-hk":"zh-hant","zh-mo":"zh-hant","zh-tw":"zh-hant"};const r=new Map([["",a]]);export function resolveMessages(e){const l=e?.trim()??"",t=r.get(l);if(t)return t;const o=function(e){const a=e.toLowerCase().split(/[-_]/).filter(Boolean),n=a[0];if(!n)return[];const i=a.slice(1),r=i.find(e=>/^[a-z]{4}$/.test(e)),l=i.find(e=>/^([a-z]{2}|\d{3})$/.test(e));return[r?`${n}-${r}`:"",l?`${n}-${l}`:"",n].filter(Boolean)}(l).map(e=>n[e]??n[i[e]??""]).find(Boolean),s=o?{link:{...a.link,...o.link},spoiler:{...a.spoiler,...o.spoiler},chat:{...a.chat,...o.chat},color:{...a.color,...o.color}}:a;return r.set(l,s),s}export function useMessages(a){return e.useMemo(()=>resolveMessages(a),[a])}
|