@noya-app/noya-designsystem 0.1.30 → 0.1.31

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.
@@ -0,0 +1,283 @@
1
+ import { Config } from "tailwindcss";
2
+
3
+ export default {
4
+ content: ["./src/**/*.{ts,tsx}"],
5
+ // if you add the dark className anywhere in the DOM tree all descendants will use dark style variables
6
+ darkMode: "class",
7
+ theme: {
8
+ extend: {
9
+ // colors should alway have a flat structure with the exception of dark/DEFAULT variables
10
+ // keys should always be kebab-case, since that's the tailwind convention
11
+ colors: {
12
+ "logo-fill": {
13
+ DEFAULT: "rgb(150, 152, 172)",
14
+ dark: "rgb(248, 248, 250)",
15
+ },
16
+ "logo-highlight": {
17
+ DEFAULT: "rgb(150, 152, 172)",
18
+ dark: "rgb(248, 248, 250)",
19
+ },
20
+ background: "rgb(255, 255, 255)",
21
+ text: {
22
+ DEFAULT: "rgb(38, 48, 83)",
23
+ dark: "rgb(248, 248, 250)",
24
+ },
25
+ "text-muted": {
26
+ DEFAULT: "rgb(107, 113, 136)",
27
+ dark: "rgb(180, 179, 182)",
28
+ },
29
+ "text-subtle": {
30
+ DEFAULT: "rgb(117, 121, 129)",
31
+ dark: "rgb(180, 179, 182)",
32
+ },
33
+ "text-disabled": {
34
+ DEFAULT: "rgb(150, 152, 172)",
35
+ dark: "rgb(100, 99, 102)",
36
+ },
37
+ "text-decorative": {
38
+ light: "rgb(168, 185, 212)",
39
+ },
40
+ "divider-subtle": {
41
+ DEFAULT: "rgba(30, 50, 100, 0.04)",
42
+ dark: "rgba(255, 255, 255, 0.04)",
43
+ },
44
+ divider: {
45
+ DEFAULT: "rgba(30, 50, 100, 0.07)",
46
+ dark: "rgba(255, 255, 255, 0.08)",
47
+ },
48
+ "divider-strong": {
49
+ DEFAULT: "rgba(30, 50, 100, 0.09)",
50
+ dark: "rgba(0, 0, 0, 1)",
51
+ },
52
+ primary: {
53
+ DEFAULT: "rgb(103, 70, 255)",
54
+ dark: "rgb(119, 66, 255)",
55
+ },
56
+ "primary-light": {
57
+ DEFAULT: "rgb(147, 86, 255)",
58
+ dark: "rgb(134, 86, 255)",
59
+ },
60
+ "primary-pastel": "rgba(234, 230, 255)",
61
+ secondary: "rgb(0, 151, 117)",
62
+ "secondary-light": "rgb(0, 160, 129)",
63
+ "secondary-pastel": "rgb(205, 238, 231)",
64
+ "secondary-bright": {
65
+ DEFAULT: "#0ab557",
66
+ dark: "#36fe91",
67
+ },
68
+ "input-background": {
69
+ DEFAULT: "rgb(240, 242, 246)",
70
+ dark: "rgba(181, 178, 255, 0.08)",
71
+ },
72
+ "input-background-light": {
73
+ DEFAULT: "rgb(243, 245, 249)",
74
+ dark: "rgba(181, 178, 255, 0.10)",
75
+ },
76
+ "code-background": {
77
+ DEFAULT: "rgb(250, 250, 250)",
78
+ dark: "rgb(20, 19, 23)",
79
+ },
80
+ "code-background-decorative-dark": "#435080",
81
+ "selected-background": "rgb(242, 245, 250)",
82
+ "breadcrumb-text": {
83
+ DEFAULT: "rgb(107, 113, 136)",
84
+ dark: "rgb(180, 179, 182)",
85
+ },
86
+ "breadcrumb-text-hover": "rgb(117, 121, 129)",
87
+ "breadcrumb-icon": "rgb(129, 131, 165)",
88
+ "canvas-background": {
89
+ DEFAULT: "rgb(249, 249, 249)",
90
+ dark: "rgb(20, 19, 23)",
91
+ },
92
+ "canvas-grid": {
93
+ DEFAULT: "rgba(0, 0, 0, 0.05)",
94
+ dark: "rgba(0, 0, 0, 0.1)",
95
+ },
96
+ "sidebar-background": {
97
+ DEFAULT: "rgb(255, 255, 255)",
98
+ dark: "rgb(34, 33, 39)",
99
+ },
100
+ "sidebar-background-transparent": {
101
+ DEFAULT: "rgba(255, 255, 255, 0.85)",
102
+ dark: "rgba(34, 33, 39, 0.95)",
103
+ },
104
+ "popover-background": {
105
+ DEFAULT: "rgb(252, 252, 252)",
106
+ dark: "rgb(34, 33, 39)",
107
+ },
108
+ "popover-divider": {
109
+ DEFAULT: "transparent",
110
+ dark: "rgba(255, 255, 255, 0.08)",
111
+ },
112
+ "listview-raised-background": {
113
+ DEFAULT: "rgba(0, 0, 0, 0.03)",
114
+ dark: "rgba(181, 178, 255, 0.1)",
115
+ },
116
+ "listview-editing-background": {
117
+ DEFAULT: "#fff",
118
+ dark: "#000",
119
+ },
120
+ "slider-background": {
121
+ DEFAULT: "white",
122
+ dark: "#BBB",
123
+ },
124
+ "slider-border": "#BBB",
125
+ "radio-group-background": {
126
+ DEFAULT: "white",
127
+ dark: "rgba(181, 178, 255, 0.08)",
128
+ },
129
+ mask: {
130
+ DEFAULT: "rgb(12, 193, 67)",
131
+ dark: "rgb(102, 187, 106)",
132
+ },
133
+ "transparent-checker": {
134
+ DEFAULT: "rgba(255, 255, 255, 0.8)",
135
+ dark: "rgba(255, 255, 255, 0.3)",
136
+ },
137
+ scrollbar: {
138
+ DEFAULT: "rgba(199, 199, 199, 0.8)",
139
+ dark: "rgba(199, 199, 199, 0.2)",
140
+ },
141
+ "placeholder-dots": {
142
+ DEFAULT: "rgba(0, 0, 0, 0.3)",
143
+ dark: "rgba(255, 255, 255, 0.3)",
144
+ },
145
+ "drag-outline": {
146
+ DEFAULT: "rgb(103, 70, 255)",
147
+ dark: "white",
148
+ },
149
+ "active-background": {
150
+ DEFAULT: "rgba(0, 0, 0, 0.1)",
151
+ dark: "rgba(181, 178, 255, 0.08)",
152
+ },
153
+ "thumbnail-background": {
154
+ DEFAULT: "#f0efff",
155
+ dark: "#1f1d33",
156
+ },
157
+ "thumbnail-shadow": {
158
+ DEFAULT: "#D3CEED66",
159
+ dark: "#1f1d3366",
160
+ },
161
+ "inline-code-text": "rgb(103, 70, 255)",
162
+ "inline-code-background": "rgb(240, 242, 246)",
163
+ "text-link": "rgb(103, 70, 255)",
164
+ "text-link-focused": "rgb(147, 86, 255)",
165
+ },
166
+ fontFamily: {
167
+ sans: [
168
+ "__Inter_6b0edc",
169
+ "__Inter_Fallback_6b0edc",
170
+ "-apple-system",
171
+ "BlinkMacSystemFont",
172
+ "Helvetica Neue",
173
+ "Segoe UI",
174
+ "Roboto",
175
+ "Helvetica",
176
+ "Arial",
177
+ "sans-serif",
178
+ ],
179
+ mono: ["Menlo", "Monaco", "Consolas", "Courier New", "monospace"],
180
+ },
181
+ // Type scale
182
+ // The last one, 0.85, I just eyeballed
183
+ // old: typeScale = [3.052, 2.441, 1.953, 1.563, 1.25, 1, 0.85]; // Major third
184
+ fontSize: {
185
+ title: [
186
+ "3.052rem",
187
+ {
188
+ lineHeight: "1.4",
189
+ letterSpacing: "-0.05em",
190
+ },
191
+ ],
192
+ subtitle: [
193
+ "1.563rem",
194
+ {
195
+ lineHeight: "1.75",
196
+ letterSpacing: "-0.05em",
197
+ },
198
+ ],
199
+ heading1: [
200
+ "1.953rem",
201
+ {
202
+ lineHeight: "1.6",
203
+ letterSpacing: "-0.025em",
204
+ },
205
+ ],
206
+ heading2: [
207
+ "1.563rem",
208
+ {
209
+ lineHeight: "1.5",
210
+ letterSpacing: "-0.025em",
211
+ },
212
+ ],
213
+ heading3: [
214
+ "1.25rem",
215
+ {
216
+ lineHeight: "1.4",
217
+ letterSpacing: "-0.025em",
218
+ },
219
+ ],
220
+ heading4: [
221
+ "1rem",
222
+ {
223
+ lineHeight: "1.4",
224
+ },
225
+ ],
226
+ heading5: [
227
+ "0.85rem",
228
+ {
229
+ lineHeight: "1.4",
230
+ },
231
+ ],
232
+ body: [
233
+ "1",
234
+ {
235
+ lineHeight: "1.4",
236
+ },
237
+ ],
238
+ small: [
239
+ "1",
240
+ {
241
+ lineHeight: "19px",
242
+ },
243
+ ],
244
+ code: [
245
+ "90%",
246
+ {
247
+ lineHeight: "1.5",
248
+ },
249
+ ],
250
+ button: [
251
+ "0.8rem",
252
+ {
253
+ lineHeight: "1.4",
254
+ letterSpacing: "-0.2px",
255
+ },
256
+ ],
257
+ label: [
258
+ "0.62rem",
259
+ {
260
+ lineHeight: "19px",
261
+ letterSpacing: "-0.3px",
262
+ },
263
+ ],
264
+ },
265
+ spacing: {
266
+ nano: "2px",
267
+ micro: "4px",
268
+ small: "8px",
269
+ medium: "16px",
270
+ large: "32px",
271
+ xlarge: "64px",
272
+ xxlarge: "128px",
273
+ "inset-top": "46px",
274
+ "sidebar-width": "260px",
275
+ "toolbar-height": "8px",
276
+ "toolbar-separator": "8px",
277
+ "inspector-h-separator": "8px",
278
+ "inspector-v-separator": "10px",
279
+ "dialog-padding": "16px",
280
+ },
281
+ },
282
+ },
283
+ } as Config;
package/tailwind.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ // Get the config from your actual config file
2
+ import tailwindConfig from "./tailwind.config";
3
+
4
+ type ExtractThemeTypes<T> = T extends { theme: { extend: infer E } }
5
+ ? E
6
+ : never;
7
+
8
+ declare module "tailwindcss/types/generated/default-theme" {
9
+ export interface DefaultTheme
10
+ extends ExtractThemeTypes<typeof tailwindConfig> {}
11
+ }
package/tsconfig.json CHANGED
@@ -1,3 +1,6 @@
1
1
  {
2
- "extends": "@repo/typescript-config/library.json"
2
+ "extends": "@repo/typescript-config/library.json",
3
+ "compilerOptions": {
4
+ "types": ["tailwindcss/types"],
5
+ },
3
6
  }
@@ -1,183 +0,0 @@
1
- import { memoize } from "@noya-app/noya-utils";
2
- import React, {
3
- createContext,
4
- CSSProperties,
5
- memo,
6
- ReactNode,
7
- useCallback,
8
- useContext,
9
- useEffect,
10
- useMemo,
11
- useRef,
12
- } from "react";
13
- import styled from "styled-components";
14
-
15
- type SelectContextValue = {
16
- addListener: (value: string, listener: () => void) => void;
17
- removeListener: (value: string) => void;
18
- };
19
-
20
- const SelectContext = createContext<SelectContextValue | undefined>(undefined);
21
-
22
- interface SelectOptionProps<T extends string> {
23
- value: T;
24
- title?: string;
25
- onSelect?: () => void;
26
- }
27
-
28
- export const SelectOption = memo(function SelectOption<T extends string>({
29
- value,
30
- title,
31
- onSelect,
32
- }: SelectOptionProps<T>) {
33
- const { addListener, removeListener } = useContext(SelectContext)!;
34
-
35
- useEffect(() => {
36
- if (!onSelect) return;
37
-
38
- addListener(value, onSelect);
39
-
40
- return () => removeListener(value);
41
- }, [addListener, onSelect, removeListener, value]);
42
-
43
- return <option value={value}>{title ?? value}</option>;
44
- });
45
-
46
- const createChevronSVGString = memoize((color: string) =>
47
- `
48
- <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 15 15' fill='${color}'>
49
- <path d='M3.13523 6.15803C3.3241 5.95657 3.64052 5.94637 3.84197 6.13523L7.5 9.56464L11.158 6.13523C11.3595 5.94637 11.6759 5.95657 11.8648 6.15803C12.0536 6.35949 12.0434 6.67591 11.842 6.86477L7.84197 10.6148C7.64964 10.7951 7.35036 10.7951 7.15803 10.6148L3.15803 6.86477C2.95657 6.67591 2.94637 6.35949 3.13523 6.15803Z'></path>
50
- </svg>
51
- `.replace(/\n/g, "")
52
- );
53
-
54
- const SelectContainer = styled.div<{ $flex: CSSProperties["flex"] }>(
55
- ({ $flex }) => ({
56
- flex: $flex ?? "1 1 0px",
57
- display: "flex",
58
- flexDirection: "row",
59
- position: "relative",
60
- })
61
- );
62
-
63
- const SelectElement = styled.select<{ $flex: CSSProperties["flex"] }>(
64
- ({ theme, $flex }) => ({
65
- appearance: "none",
66
- ...theme.textStyles.small,
67
- color: theme.colors.text,
68
- lineHeight: "19px",
69
- width: "0px", // Reset intrinsic width
70
- flex: $flex ?? "1 1 0px",
71
- position: "relative",
72
- border: "0",
73
- outline: "none",
74
- minWidth: "0",
75
- textAlign: "left",
76
- alignSelf: "stretch",
77
- borderRadius: "4px",
78
- paddingTop: "4px",
79
- paddingBottom: "4px",
80
- paddingLeft: "6px",
81
- paddingRight: "23px",
82
- background: [
83
- `calc(100% - 6px) / 15px url("data:image/svg+xml;utf8,${createChevronSVGString(
84
- theme.colors.icon
85
- )}") no-repeat`,
86
- theme.colors.inputBackground,
87
- ].join(","),
88
- "&:focus": {
89
- boxShadow: `0 0 0 2px ${theme.colors.primary}`,
90
- },
91
- })
92
- );
93
-
94
- const SelectLabel = styled.label(({ theme }) => ({
95
- ...theme.textStyles.label,
96
- color: theme.colors.textDisabled,
97
- lineHeight: "19px",
98
- position: "absolute",
99
- inset: "0",
100
- fontWeight: "bold",
101
- fontSize: "60%",
102
- pointerEvents: "none",
103
- display: "flex",
104
- alignItems: "center",
105
- justifyContent: "end",
106
- paddingRight: "24px",
107
- }));
108
-
109
- type ChildrenProps<T> =
110
- | {
111
- children: ReactNode;
112
- }
113
- | {
114
- options: readonly T[];
115
- getTitle?: (option: T, index: number) => string;
116
- onChange: (value: T) => void;
117
- };
118
-
119
- type Props<T extends string> = ChildrenProps<T> & {
120
- id: string;
121
- flex?: CSSProperties["flex"];
122
- value: T;
123
- label?: ReactNode;
124
- };
125
-
126
- export const Select = memo(function Select<T extends string>({
127
- id,
128
- flex,
129
- value,
130
- label,
131
- ...rest
132
- }: Props<T>) {
133
- const options = "options" in rest ? rest.options : undefined;
134
- const getTitle = "options" in rest ? rest.getTitle : undefined;
135
- const onChange = "options" in rest ? rest.onChange : undefined;
136
- const children = "options" in rest ? undefined : rest.children;
137
-
138
- const optionElements = useMemo(
139
- () =>
140
- options
141
- ? options.map((option, index) => (
142
- <SelectOption
143
- key={option}
144
- value={option}
145
- title={getTitle?.(option, index)}
146
- onSelect={() => onChange?.(option)}
147
- />
148
- ))
149
- : children,
150
- [children, getTitle, onChange, options]
151
- );
152
-
153
- const listeners = useRef<Map<string, () => void>>(new Map());
154
-
155
- const contextValue: SelectContextValue = useMemo(
156
- () => ({
157
- addListener: (value, listener) => listeners.current.set(value, listener),
158
- removeListener: (value) => listeners.current.delete(value),
159
- }),
160
- []
161
- );
162
-
163
- return (
164
- <SelectContext.Provider value={contextValue}>
165
- <SelectContainer $flex={flex}>
166
- <SelectElement
167
- id={id}
168
- $flex={flex}
169
- value={value}
170
- onChange={useCallback(
171
- (event: React.ChangeEvent<HTMLSelectElement>) =>
172
- listeners.current.get(event.target.value)?.(),
173
- [listeners]
174
- )}
175
- onPointerDown={(event) => event.stopPropagation()}
176
- >
177
- {optionElements}
178
- </SelectElement>
179
- </SelectContainer>
180
- {label && <SelectLabel htmlFor={id}>{label}</SelectLabel>}
181
- </SelectContext.Provider>
182
- );
183
- });