@noya-app/noya-designsystem 0.0.2
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/.turbo/turbo-build.log +17 -0
- package/CHANGELOG.md +7 -0
- package/README.md +1 -0
- package/dist/index.d.mts +1178 -0
- package/dist/index.d.ts +1178 -0
- package/dist/index.js +15651 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +15684 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +49 -0
- package/src/__tests__/__snapshots__/fuzzyScorer.test.ts.snap +41 -0
- package/src/__tests__/fuzzyScorer.test.ts +85 -0
- package/src/components/ActivityIndicator.tsx +44 -0
- package/src/components/Avatar.tsx +64 -0
- package/src/components/Button.tsx +209 -0
- package/src/components/Chip.tsx +214 -0
- package/src/components/ContextMenu.tsx +224 -0
- package/src/components/Dialog.tsx +143 -0
- package/src/components/Divider.tsx +40 -0
- package/src/components/DropdownMenu.tsx +208 -0
- package/src/components/FillInputField.tsx +43 -0
- package/src/components/FillPreviewBackground.tsx +132 -0
- package/src/components/GradientPicker.tsx +88 -0
- package/src/components/Grid.tsx +54 -0
- package/src/components/GridView.tsx +468 -0
- package/src/components/IconButton.tsx +46 -0
- package/src/components/InputField.tsx +568 -0
- package/src/components/InputFieldWithCompletions.tsx +477 -0
- package/src/components/Label.tsx +62 -0
- package/src/components/LabeledElementView.tsx +185 -0
- package/src/components/ListView.tsx +950 -0
- package/src/components/Popover.tsx +113 -0
- package/src/components/Progress.tsx +57 -0
- package/src/components/RadioGroup.tsx +163 -0
- package/src/components/ScrollArea.tsx +70 -0
- package/src/components/Select.tsx +152 -0
- package/src/components/Slider.tsx +82 -0
- package/src/components/Sortable.tsx +296 -0
- package/src/components/Spacer.tsx +29 -0
- package/src/components/Stack.tsx +142 -0
- package/src/components/Switch.tsx +67 -0
- package/src/components/Text.tsx +164 -0
- package/src/components/Toast.tsx +86 -0
- package/src/components/Tooltip.tsx +43 -0
- package/src/components/TreeView.tsx +85 -0
- package/src/components/internal/Menu.tsx +222 -0
- package/src/components/internal/TextInput.tsx +296 -0
- package/src/components/internal/__tests__/TextInput.test.tsx +144 -0
- package/src/contexts/DesignSystemConfiguration.tsx +57 -0
- package/src/contexts/DialogContext.tsx +190 -0
- package/src/contexts/GlobalInputBlurContext.tsx +61 -0
- package/src/contexts/ImageDataContext.tsx +44 -0
- package/src/hooks/__tests__/mergeEventHandlers.test.ts +47 -0
- package/src/hooks/mergeEventHandlers.ts +55 -0
- package/src/hooks/useHover.ts +173 -0
- package/src/hooks/useObjectURL.ts +22 -0
- package/src/hooks/usePlatform.ts +13 -0
- package/src/index.tsx +77 -0
- package/src/mediaQuery.ts +13 -0
- package/src/theme/dark.ts +37 -0
- package/src/theme/index.ts +17 -0
- package/src/theme/light.ts +178 -0
- package/src/utils/breakpoints.ts +45 -0
- package/src/utils/completions.ts +21 -0
- package/src/utils/createSectionedMenu.ts +38 -0
- package/src/utils/fuzzyScorer.ts +105 -0
- package/src/utils/getGradientBackground.tsx +33 -0
- package/src/utils/handleNudge.ts +30 -0
- package/src/utils/mouseEvent.ts +7 -0
- package/src/utils/sketchColor.ts +34 -0
- package/src/utils/sketchPattern.ts +29 -0
- package/src/utils/withSeparatorElements.ts +31 -0
- package/tsconfig.json +3 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createContext, useContext, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
export type GlobalInputBlurContextValue = {
|
|
4
|
+
addListener: (f: () => void) => void;
|
|
5
|
+
removeListener: (f: () => void) => void;
|
|
6
|
+
trigger: () => void;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const defaultValue = (() => {
|
|
10
|
+
const listeners: (() => void)[] = [];
|
|
11
|
+
|
|
12
|
+
const value: GlobalInputBlurContextValue = {
|
|
13
|
+
addListener(f) {
|
|
14
|
+
listeners.push(f);
|
|
15
|
+
},
|
|
16
|
+
removeListener(f) {
|
|
17
|
+
const index = listeners.indexOf(f);
|
|
18
|
+
if (index === -1) return;
|
|
19
|
+
listeners.splice(index, 1);
|
|
20
|
+
},
|
|
21
|
+
trigger() {
|
|
22
|
+
listeners.forEach((f) => {
|
|
23
|
+
f();
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return value;
|
|
29
|
+
})();
|
|
30
|
+
|
|
31
|
+
const GlobalInputBlurContext =
|
|
32
|
+
createContext<GlobalInputBlurContextValue>(defaultValue);
|
|
33
|
+
|
|
34
|
+
export const GlobalInputBlurProvider = GlobalInputBlurContext.Provider;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Some components store their editable state internally.
|
|
38
|
+
* We trigger this event before selection changes so that they can commit their
|
|
39
|
+
* edits before they unmount. This is more manual but simpler than doing so during
|
|
40
|
+
* the unmount phase (e.g. the return function of `useEffect`), since handlers are
|
|
41
|
+
* guaranteed to be called with the current data.
|
|
42
|
+
*/
|
|
43
|
+
export const useGlobalInputBlur = (): GlobalInputBlurContextValue => {
|
|
44
|
+
return useContext(GlobalInputBlurContext);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export function useGlobalInputBlurListener(f: () => void) {
|
|
48
|
+
const context = useGlobalInputBlur();
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
context.addListener(f);
|
|
52
|
+
|
|
53
|
+
return () => {
|
|
54
|
+
context.removeListener(f);
|
|
55
|
+
};
|
|
56
|
+
}, [context, f]);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function useGlobalInputBlurTrigger() {
|
|
60
|
+
return useGlobalInputBlur().trigger;
|
|
61
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
createContext,
|
|
3
|
+
memo,
|
|
4
|
+
ReactNode,
|
|
5
|
+
useContext,
|
|
6
|
+
useMemo,
|
|
7
|
+
} from "react";
|
|
8
|
+
|
|
9
|
+
export type ImageDataContextValue = {
|
|
10
|
+
getImageData: (ref: string) => ArrayBuffer | undefined;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const ImageDataContext = createContext<ImageDataContextValue | undefined>(
|
|
14
|
+
undefined
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
export const ImageDataProvider = memo(function ImageDataProvider({
|
|
18
|
+
children,
|
|
19
|
+
getImageData,
|
|
20
|
+
}: {
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
getImageData?: (ref: string) => ArrayBuffer | undefined;
|
|
23
|
+
}) {
|
|
24
|
+
const contextValue = useMemo(
|
|
25
|
+
() => ({ getImageData: getImageData ?? (() => undefined) }),
|
|
26
|
+
[getImageData]
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<ImageDataContext.Provider value={contextValue}>
|
|
31
|
+
{children}
|
|
32
|
+
</ImageDataContext.Provider>
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export function useImageData(ref: string): ArrayBuffer | undefined {
|
|
37
|
+
const value = useContext(ImageDataContext);
|
|
38
|
+
|
|
39
|
+
if (!value) {
|
|
40
|
+
throw new Error("Missing ImageDataProvider");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return value.getImageData(ref);
|
|
44
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ReactEventHandlers } from 'react-use-gesture/dist/types';
|
|
2
|
+
import { mergeEventHandlers } from '../..';
|
|
3
|
+
|
|
4
|
+
const event = { defaultPrevented: false };
|
|
5
|
+
|
|
6
|
+
test('merges handlers', () => {
|
|
7
|
+
const out: [number, any][] = [];
|
|
8
|
+
|
|
9
|
+
const a: ReactEventHandlers = { onClick: (e) => out.push([1, e]) };
|
|
10
|
+
const b: ReactEventHandlers = { onClick: (e) => out.push([2, e]) };
|
|
11
|
+
const c: ReactEventHandlers = {};
|
|
12
|
+
const d: ReactEventHandlers = { onClick: (e) => out.push([3, e]) };
|
|
13
|
+
|
|
14
|
+
const result = mergeEventHandlers(a, b, c, d);
|
|
15
|
+
|
|
16
|
+
result.onClick?.(event as any);
|
|
17
|
+
|
|
18
|
+
expect(Object.keys(result)).toEqual(['onClick']);
|
|
19
|
+
expect(typeof result.onClick).toEqual('function');
|
|
20
|
+
expect(out).toEqual([
|
|
21
|
+
[1, event],
|
|
22
|
+
[2, event],
|
|
23
|
+
[3, event],
|
|
24
|
+
]);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const noop = () => {};
|
|
28
|
+
|
|
29
|
+
test('merges multiple handlers', () => {
|
|
30
|
+
const a: ReactEventHandlers = {
|
|
31
|
+
onMouseDown: noop,
|
|
32
|
+
onMouseMove: noop,
|
|
33
|
+
onMouseUp: noop,
|
|
34
|
+
};
|
|
35
|
+
const b: ReactEventHandlers = {
|
|
36
|
+
onMouseDown: noop,
|
|
37
|
+
onMouseUp: noop,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const result = mergeEventHandlers(a, b);
|
|
41
|
+
|
|
42
|
+
expect(Object.keys(result)).toEqual([
|
|
43
|
+
'onMouseDown',
|
|
44
|
+
'onMouseMove',
|
|
45
|
+
'onMouseUp',
|
|
46
|
+
]);
|
|
47
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { KeyMap } from '@noya-app/noya-keymap';
|
|
2
|
+
import { unique } from '@noya-app/noya-utils';
|
|
3
|
+
import { composeEventHandlers } from '@radix-ui/primitive';
|
|
4
|
+
import { useGesture } from 'react-use-gesture';
|
|
5
|
+
import { RegularMenuItem } from '../components/internal/Menu';
|
|
6
|
+
import { Optional } from '../utils/createSectionedMenu';
|
|
7
|
+
|
|
8
|
+
function composeAllEventHandlers<E>(...handlers: ((e: E) => void)[]) {
|
|
9
|
+
const [first, ...rest] = handlers;
|
|
10
|
+
|
|
11
|
+
return rest.reduce(
|
|
12
|
+
(result, handler) => composeEventHandlers(result, handler),
|
|
13
|
+
first,
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type ReactDOMEventHandlers = ReturnType<
|
|
18
|
+
ReturnType<typeof useGesture>
|
|
19
|
+
> & {
|
|
20
|
+
onKeyDown?: (e: React.KeyboardEvent) => void;
|
|
21
|
+
onKeyDownCapture?: (e: React.KeyboardEvent) => void;
|
|
22
|
+
onKeyUp?: (e: React.KeyboardEvent) => void;
|
|
23
|
+
onKeyUpCapture?: (e: React.KeyboardEvent) => void;
|
|
24
|
+
onKeyPress?: (e: React.KeyboardEvent) => void;
|
|
25
|
+
onKeyPressCapture?: (e: React.KeyboardEvent) => void;
|
|
26
|
+
|
|
27
|
+
// Special
|
|
28
|
+
onBeforeInput?: (e: InputEvent) => void;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type ReactEventHandlers<MenuItemType extends string = string> =
|
|
32
|
+
ReactDOMEventHandlers & {
|
|
33
|
+
onContributeMenuItems?: () => Optional<RegularMenuItem<MenuItemType>>[];
|
|
34
|
+
keyboardShortcuts?: KeyMap;
|
|
35
|
+
onSelectMenuItem?: (id: string) => void;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type EventName = keyof ReactEventHandlers;
|
|
39
|
+
|
|
40
|
+
export function mergeEventHandlers(
|
|
41
|
+
...handlerMaps: ReactEventHandlers[]
|
|
42
|
+
): ReactEventHandlers {
|
|
43
|
+
const eventNames = unique(handlerMaps.map(Object.keys).flat() as EventName[]);
|
|
44
|
+
|
|
45
|
+
return Object.fromEntries(
|
|
46
|
+
eventNames.map((eventName) => {
|
|
47
|
+
const handlers = handlerMaps.flatMap((handlerMap) => {
|
|
48
|
+
const value = handlerMap[eventName];
|
|
49
|
+
return value ? [value] : [];
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return [eventName, composeAllEventHandlers(...handlers)];
|
|
53
|
+
}),
|
|
54
|
+
);
|
|
55
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// From: https://github.com/radix-ui/primitives/blob/main/packages/react/scroll-area/src/useHover.ts
|
|
2
|
+
// MIT License
|
|
3
|
+
|
|
4
|
+
// This code is a fork of `react-aria`'s `useHover` hook. The code in this file are based on code
|
|
5
|
+
// from both `react-aria` and `react-interactions`. Original licensing for each project can be found
|
|
6
|
+
// in the the root directories of each respective repository.
|
|
7
|
+
//
|
|
8
|
+
// @see https://github.com/adobe/react-spectrum/tree/452d1cb6a49f9f493757737edaf2b64014108de6/packages/%40react-aria
|
|
9
|
+
// @see https://github.com/facebook/react/tree/cc7c1aece46a6b69b41958d731e0fd27c94bfc6c/packages/react-interactions
|
|
10
|
+
|
|
11
|
+
import * as React from 'react';
|
|
12
|
+
|
|
13
|
+
export interface HoverEvent {
|
|
14
|
+
/** The type of hover event being fired. */
|
|
15
|
+
type: 'hoverstart' | 'hoverend';
|
|
16
|
+
/** The pointer type that triggered the hover event. */
|
|
17
|
+
pointerType: 'mouse' | 'pen';
|
|
18
|
+
/** The target element of the hover event. */
|
|
19
|
+
target: HTMLElement;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface HoverEvents {
|
|
23
|
+
/** Handler that is called when a hover interaction starts. */
|
|
24
|
+
onHoverStart?(e: HoverEvent): void;
|
|
25
|
+
/** Handler that is called when a hover interaction ends. */
|
|
26
|
+
onHoverEnd?(e: HoverEvent): void;
|
|
27
|
+
/** Handler that is called when the hover state changes. */
|
|
28
|
+
onHoverChange?(isHovering: boolean): void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface HoverProps extends HoverEvents {
|
|
32
|
+
/** Whether the hover events should be disabled. */
|
|
33
|
+
isDisabled?: boolean;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface HoverResult {
|
|
37
|
+
/** Props to spread on the target element. */
|
|
38
|
+
hoverProps: React.HTMLAttributes<HTMLElement>;
|
|
39
|
+
isHovered: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// iOS fires onPointerEnter twice: once with pointerType="touch" and again with pointerType="mouse".
|
|
43
|
+
// We want to ignore these emulated events so they do not trigger hover behavior.
|
|
44
|
+
// See https://bugs.webkit.org/show_bug.cgi?id=214609.
|
|
45
|
+
let globalIgnoreEmulatedMouseEvents = false;
|
|
46
|
+
let hoverCount = 0;
|
|
47
|
+
|
|
48
|
+
function setGlobalIgnoreEmulatedMouseEvents() {
|
|
49
|
+
globalIgnoreEmulatedMouseEvents = true;
|
|
50
|
+
|
|
51
|
+
// Clear globalIgnoreEmulatedMouseEvents after a short timeout. iOS fires onPointerEnter with
|
|
52
|
+
// pointerType="mouse" immediately after onPointerUp and before onFocus. On other devices that
|
|
53
|
+
// don't have this quirk, we don't want to ignore a mouse hover sometime in the distant future
|
|
54
|
+
// because a user previously touched the element.
|
|
55
|
+
setTimeout(function clearGlobalIgnoreEmulatedMouseEvents() {
|
|
56
|
+
globalIgnoreEmulatedMouseEvents = false;
|
|
57
|
+
}, 50);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function handleGlobalPointerEvent(event: PointerEvent) {
|
|
61
|
+
if (event.pointerType === 'touch') {
|
|
62
|
+
setGlobalIgnoreEmulatedMouseEvents();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function setupGlobalTouchEvents() {
|
|
67
|
+
if (typeof document === 'undefined') {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
document.addEventListener('pointerup', handleGlobalPointerEvent);
|
|
72
|
+
|
|
73
|
+
hoverCount++;
|
|
74
|
+
return function teardownGlobalTouchEvents() {
|
|
75
|
+
hoverCount--;
|
|
76
|
+
if (hoverCount > 0) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
document.removeEventListener('pointerup', handleGlobalPointerEvent);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Handles pointer hover interactions for an element. Normalizes behavior
|
|
85
|
+
* across browsers and platforms, and ignores emulated mouse events on touch devices.
|
|
86
|
+
*/
|
|
87
|
+
export function useHover(props: HoverProps = {}): HoverResult {
|
|
88
|
+
const { onHoverStart, onHoverChange, onHoverEnd, isDisabled } = props;
|
|
89
|
+
|
|
90
|
+
const [isHovered, setHovered] = React.useState(false);
|
|
91
|
+
const state = React.useRef({
|
|
92
|
+
isHovered: false,
|
|
93
|
+
ignoreEmulatedMouseEvents: false,
|
|
94
|
+
}).current;
|
|
95
|
+
|
|
96
|
+
React.useEffect(setupGlobalTouchEvents, []);
|
|
97
|
+
|
|
98
|
+
const hoverProps = React.useMemo(
|
|
99
|
+
function getHoverProps() {
|
|
100
|
+
function triggerHoverStart(
|
|
101
|
+
event: PointerEvent | React.PointerEvent | MouseEvent | React.MouseEvent,
|
|
102
|
+
pointerType: 'mouse' | 'pen' | 'touch'
|
|
103
|
+
) {
|
|
104
|
+
if (isDisabled || pointerType === 'touch' || state.isHovered) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
state.isHovered = true;
|
|
109
|
+
const target = event.target as HTMLElement;
|
|
110
|
+
|
|
111
|
+
if (onHoverStart) {
|
|
112
|
+
onHoverStart({
|
|
113
|
+
type: 'hoverstart',
|
|
114
|
+
target,
|
|
115
|
+
pointerType,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (onHoverChange) {
|
|
120
|
+
onHoverChange(true);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
setHovered(true);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function triggerHoverEnd(
|
|
127
|
+
event: PointerEvent | React.PointerEvent | MouseEvent | React.MouseEvent,
|
|
128
|
+
pointerType: 'mouse' | 'pen' | 'touch'
|
|
129
|
+
) {
|
|
130
|
+
if (isDisabled || pointerType === 'touch' || !state.isHovered) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
state.isHovered = false;
|
|
135
|
+
const target = event.target as HTMLElement;
|
|
136
|
+
|
|
137
|
+
if (onHoverEnd) {
|
|
138
|
+
onHoverEnd({
|
|
139
|
+
type: 'hoverend',
|
|
140
|
+
target,
|
|
141
|
+
pointerType,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (onHoverChange) {
|
|
146
|
+
onHoverChange(false);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
setHovered(false);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const hoverProps: React.HTMLAttributes<HTMLElement> = {
|
|
153
|
+
onPointerEnter(event: React.PointerEvent<HTMLElement>) {
|
|
154
|
+
if (globalIgnoreEmulatedMouseEvents && event.pointerType === 'mouse') {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
triggerHoverStart(event, event.pointerType);
|
|
158
|
+
},
|
|
159
|
+
onPointerLeave(event: React.PointerEvent<HTMLElement>) {
|
|
160
|
+
triggerHoverEnd(event, event.pointerType);
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
return hoverProps;
|
|
165
|
+
},
|
|
166
|
+
[isDisabled, state, onHoverStart, onHoverChange, onHoverEnd]
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
return {
|
|
170
|
+
hoverProps,
|
|
171
|
+
isHovered,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useLayoutEffect, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
export function useObjectURL(object?: ArrayBuffer | Uint8Array | Blob) {
|
|
4
|
+
const objectURL = useMemo(() => {
|
|
5
|
+
if (object instanceof Blob) return URL.createObjectURL(object);
|
|
6
|
+
|
|
7
|
+
const bytes =
|
|
8
|
+
object instanceof Uint8Array
|
|
9
|
+
? object
|
|
10
|
+
: object !== undefined
|
|
11
|
+
? new Uint8Array(object)
|
|
12
|
+
: new Uint8Array();
|
|
13
|
+
|
|
14
|
+
return URL.createObjectURL(new Blob([bytes]));
|
|
15
|
+
}, [object]);
|
|
16
|
+
|
|
17
|
+
useLayoutEffect(() => {
|
|
18
|
+
return () => URL.revokeObjectURL(objectURL);
|
|
19
|
+
}, [objectURL]);
|
|
20
|
+
|
|
21
|
+
return objectURL;
|
|
22
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useDesignSystemConfiguration } from '../contexts/DesignSystemConfiguration';
|
|
2
|
+
|
|
3
|
+
export function usePlatform() {
|
|
4
|
+
return useDesignSystemConfiguration().platform;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Either ctrl or meta, depending on the platform
|
|
9
|
+
*/
|
|
10
|
+
export function usePlatformModKey(): 'ctrlKey' | 'metaKey' {
|
|
11
|
+
const platform = useDesignSystemConfiguration().platform;
|
|
12
|
+
return platform === 'mac' ? 'metaKey' : 'ctrlKey';
|
|
13
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// These types propagate generics through memo and forwardRef to support generic components
|
|
2
|
+
//
|
|
3
|
+
// https://stackoverflow.com/questions/60386614/how-to-use-props-with-generics-with-react-memo/60389122#60389122
|
|
4
|
+
// https://stackoverflow.com/questions/58469229/react-with-typescript-generics-while-using-react-forwardref/58473012
|
|
5
|
+
declare module "react" {
|
|
6
|
+
function memo<A, B>(
|
|
7
|
+
Component: (props: A) => B
|
|
8
|
+
): (props: A) => React.ReactElement | null;
|
|
9
|
+
|
|
10
|
+
function forwardRef<T, P = {}>(
|
|
11
|
+
render: (props: P, ref: React.ForwardedRef<T>) => React.ReactElement | null
|
|
12
|
+
): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Theme
|
|
16
|
+
// Components
|
|
17
|
+
export * from "./components/ActivityIndicator";
|
|
18
|
+
export * from "./components/Avatar";
|
|
19
|
+
export * from "./components/Button";
|
|
20
|
+
export * from "./components/Chip";
|
|
21
|
+
export * from "./components/ContextMenu";
|
|
22
|
+
export * from "./components/Dialog";
|
|
23
|
+
export * from "./components/Divider";
|
|
24
|
+
export * from "./components/DropdownMenu";
|
|
25
|
+
export * from "./components/FillInputField";
|
|
26
|
+
export * from "./components/FillPreviewBackground";
|
|
27
|
+
export * from "./components/GradientPicker";
|
|
28
|
+
export * from "./components/Grid";
|
|
29
|
+
export * from "./components/GridView";
|
|
30
|
+
export * from "./components/IconButton";
|
|
31
|
+
export * from "./components/InputField";
|
|
32
|
+
export * from "./components/InputFieldWithCompletions";
|
|
33
|
+
export * from "./components/Label";
|
|
34
|
+
export * from "./components/LabeledElementView";
|
|
35
|
+
export * from "./components/ListView";
|
|
36
|
+
export * from "./components/Popover";
|
|
37
|
+
export * from "./components/Progress";
|
|
38
|
+
export * from "./components/RadioGroup";
|
|
39
|
+
export * from "./components/ScrollArea";
|
|
40
|
+
export * from "./components/Select";
|
|
41
|
+
export * from "./components/Slider";
|
|
42
|
+
export * from "./components/Sortable";
|
|
43
|
+
export type { RelativeDropPosition } from "./components/Sortable";
|
|
44
|
+
export * from "./components/Spacer";
|
|
45
|
+
export * from "./components/Stack";
|
|
46
|
+
export * from "./components/Switch";
|
|
47
|
+
export * from "./components/Text";
|
|
48
|
+
export * from "./components/Toast";
|
|
49
|
+
export * from "./components/Tooltip";
|
|
50
|
+
export * from "./components/TreeView";
|
|
51
|
+
export { KeyboardShortcut, SEPARATOR_ITEM } from "./components/internal/Menu";
|
|
52
|
+
export type {
|
|
53
|
+
ExtractMenuItemType,
|
|
54
|
+
MenuItem,
|
|
55
|
+
RegularMenuItem,
|
|
56
|
+
} from "./components/internal/Menu";
|
|
57
|
+
export * from "./contexts/DesignSystemConfiguration";
|
|
58
|
+
export * from "./contexts/DialogContext";
|
|
59
|
+
// Contexts
|
|
60
|
+
export * from "./contexts/GlobalInputBlurContext";
|
|
61
|
+
// Hooks
|
|
62
|
+
export * from "./hooks/mergeEventHandlers";
|
|
63
|
+
export * from "./hooks/useHover";
|
|
64
|
+
export * from "./hooks/usePlatform";
|
|
65
|
+
export * from "./mediaQuery";
|
|
66
|
+
export * from "./theme";
|
|
67
|
+
export * as darkTheme from "./theme/dark";
|
|
68
|
+
export * as lightTheme from "./theme/light";
|
|
69
|
+
export * from "./utils/createSectionedMenu";
|
|
70
|
+
export * from "./utils/getGradientBackground";
|
|
71
|
+
export * from "./utils/mouseEvent";
|
|
72
|
+
// Utils
|
|
73
|
+
export * from "./utils/completions";
|
|
74
|
+
export * from "./utils/fuzzyScorer";
|
|
75
|
+
export * from "./utils/sketchColor";
|
|
76
|
+
export * from "./utils/sketchPattern";
|
|
77
|
+
export { default as withSeparatorElements } from "./utils/withSeparatorElements";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const size = {
|
|
2
|
+
medium: '800px',
|
|
3
|
+
large: '1280px',
|
|
4
|
+
xlarge: '1550px',
|
|
5
|
+
xxlarge: '1680px',
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const mediaQuery = {
|
|
9
|
+
small: `@media (max-width: ${size.medium})`,
|
|
10
|
+
medium: `@media (max-width: ${size.large}) and (min-width: ${size.medium})`,
|
|
11
|
+
large: `@media (max-width: ${size.xlarge}) and (min-width: ${size.large})`,
|
|
12
|
+
xlarge: `@media (min-width: ${size.xlarge})`,
|
|
13
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import produce from 'immer';
|
|
2
|
+
import * as lightTheme from './light';
|
|
3
|
+
|
|
4
|
+
export const colors = produce(lightTheme.colors, (colors) => {
|
|
5
|
+
colors.logo.fill = 'rgb(248,248,250)';
|
|
6
|
+
colors.logo.highlight = 'rgb(248,248,250)';
|
|
7
|
+
colors.text = 'rgb(248,248,250)';
|
|
8
|
+
colors.textMuted = 'rgb(180,179,182)';
|
|
9
|
+
colors.textDisabled = 'rgb(100,99,102)';
|
|
10
|
+
colors.inputBackground = 'rgba(181,178,255,0.08)';
|
|
11
|
+
colors.inputBackgroundLight = 'rgba(181,178,255,0.10)';
|
|
12
|
+
colors.codeBackground = 'rgb(20,19,23)';
|
|
13
|
+
colors.dividerSubtle = 'rgba(255,255,255,0.04)';
|
|
14
|
+
colors.divider = 'rgba(255,255,255,0.08)';
|
|
15
|
+
colors.dividerStrong = 'rgba(0,0,0,1)';
|
|
16
|
+
colors.primary = 'rgb(119, 66, 255)';
|
|
17
|
+
colors.primaryLight = 'rgb(134, 86, 255)';
|
|
18
|
+
colors.secondaryBright = '#36fe91';
|
|
19
|
+
colors.canvas.background = 'rgb(20,19,23)';
|
|
20
|
+
colors.canvas.sliceOutline = 'rgb(150,150,150)';
|
|
21
|
+
colors.canvas.grid = 'rgba(0,0,0,0.1)';
|
|
22
|
+
colors.sidebar.background = 'rgba(34,33,39,0.95)';
|
|
23
|
+
colors.popover.background = 'rgba(34,33,39,1)';
|
|
24
|
+
colors.popover.divider = colors.divider;
|
|
25
|
+
colors.listView.raisedBackground = 'rgba(181,178,255,0.1)';
|
|
26
|
+
colors.listView.editingBackground = '#000';
|
|
27
|
+
colors.slider.background = '#BBB';
|
|
28
|
+
colors.radioGroup.background = colors.inputBackground;
|
|
29
|
+
colors.mask = 'rgb(102,187,106)';
|
|
30
|
+
colors.transparentChecker = 'rgba(255,255,255,0.3)';
|
|
31
|
+
colors.scrollbar = 'rgba(199,199,199,0.2)';
|
|
32
|
+
colors.placeholderDots = 'rgba(255,255,255,0.3)';
|
|
33
|
+
colors.dragOutline = 'white';
|
|
34
|
+
colors.activeBackground = 'rgba(181,178,255,0.08)';
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export { fonts, sizes, textStyles } from './light';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as lightTheme from './light';
|
|
2
|
+
|
|
3
|
+
export type Theme = typeof lightTheme;
|
|
4
|
+
|
|
5
|
+
declare module 'styled-components' {
|
|
6
|
+
export interface DefaultTheme extends Theme {}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface Colors {}
|
|
10
|
+
|
|
11
|
+
type PickByValue<T, V> = Pick<
|
|
12
|
+
T,
|
|
13
|
+
// Remove any value that extends V
|
|
14
|
+
{ [K in keyof T]-?: T[K] extends V ? K : never }[keyof T]
|
|
15
|
+
>;
|
|
16
|
+
|
|
17
|
+
export type ThemeColorName = keyof PickByValue<Theme['colors'], string>;
|