@fibery/ui-kit 1.40.3 → 1.41.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/CHANGELOG.md +14 -0
- package/eslint.config.mjs +16 -0
- package/package.json +11 -13
- package/src/a11y-color.test.ts +162 -0
- package/src/a11y-color.ts +13 -20
- package/src/actions-menu/actions-menu-item.tsx +9 -12
- package/src/actions-menu/context-actions-menu.tsx +8 -3
- package/src/ai/model.tsx +0 -1
- package/src/ai/temperature.tsx +0 -1
- package/src/animated-height-container.tsx +3 -3
- package/src/antd/ant-modal.tsx +10 -5
- package/src/antd/index.tsx +3 -0
- package/src/antd/input.tsx +0 -1
- package/src/antd/styles.ts +15 -15
- package/src/app-icon-with-fallback.tsx +6 -6
- package/src/app-icon-wrapper.tsx +2 -3
- package/src/app-icon.tsx +4 -4
- package/src/avatar.tsx +3 -3
- package/src/button/base-button.tsx +0 -1
- package/src/button/button.tsx +1 -0
- package/src/button/make-button-colors.ts +5 -13
- package/src/checkbox.tsx +2 -2
- package/src/collapsible-section.tsx +11 -1
- package/src/color-picker/ColorPickerOrLoader.js +2 -2
- package/src/color-utils.test.ts +307 -0
- package/src/color-utils.ts +215 -0
- package/src/command-menu/index.tsx +1 -0
- package/src/comment.tsx +3 -2
- package/src/context-menu/index.tsx +12 -7
- package/src/date-picker/contexts.ts +2 -2
- package/src/date-picker/date-range-picker.tsx +3 -4
- package/src/date-picker/single-date-picker.tsx +147 -119
- package/src/date-picker/types.ts +4 -3
- package/src/day-select/iso-week-day-select.tsx +2 -2
- package/src/day-select/week-day-select.tsx +2 -2
- package/src/delayed.tsx +0 -1
- package/src/design-system/alpha.test.ts +59 -0
- package/src/design-system/alpha.ts +41 -0
- package/src/design-system/animation.ts +10 -0
- package/src/design-system/colors-css.test.ts +39 -0
- package/src/design-system/colors-css.ts +25 -0
- package/src/design-system/colors-js.test.ts +232 -0
- package/src/design-system/colors-js.ts +107 -0
- package/src/design-system/colors.test.ts +74 -0
- package/src/design-system/colors.ts +759 -0
- package/src/design-system/date.ts +3 -0
- package/src/design-system/layout.ts +106 -0
- package/src/design-system/theme.test.ts +94 -0
- package/src/design-system/theme.ts +93 -0
- package/src/design-system/typography.ts +179 -0
- package/src/design-system/vars.test.ts +1679 -0
- package/src/design-system/vars.ts +52 -0
- package/src/design-system.test.ts +45 -22
- package/src/design-system.ts +25 -1339
- package/src/dropdown-menu/index.tsx +21 -16
- package/src/emoji-picker/app-icon-picker.tsx +5 -5
- package/src/emoji-picker/emoji-picker-content-with-color.tsx +4 -4
- package/src/emoji-picker/icon-emoji-picker.tsx +3 -3
- package/src/favorites-icon.tsx +1 -1
- package/src/fibermoji-placeholder.tsx +3 -3
- package/src/field-container.tsx +11 -2
- package/src/file-item/file-icon.tsx +169 -0
- package/src/file-item/file-menu-items.tsx +68 -0
- package/src/file-item/file-preview-actions.tsx +38 -0
- package/src/file-item/file-title.tsx +48 -0
- package/src/file-item/types.ts +27 -0
- package/src/file-item/use-register-in-image-gallery.tsx +70 -0
- package/src/file-item-2.tsx +32 -348
- package/src/file-item.tsx +6 -3
- package/src/hue-shift.test.ts +91 -0
- package/src/icons/ast/ChatBubble.ts +8 -0
- package/src/icons/ast/ChatFloat.ts +8 -0
- package/src/icons/ast/ChatSidebar.ts +8 -0
- package/src/icons/ast/FileCounter.ts +8 -0
- package/src/icons/ast/FileMultiple.ts +8 -0
- package/src/icons/ast/FileOther.ts +8 -0
- package/src/icons/ast/NoBorder.ts +8 -0
- package/src/icons/ast/NoFill.ts +8 -0
- package/src/icons/ast/ValueEdit.ts +8 -0
- package/src/icons/ast/index.tsx +9 -0
- package/src/icons/react/ChatBubble.tsx +13 -0
- package/src/icons/react/ChatFloat.tsx +13 -0
- package/src/icons/react/ChatSidebar.tsx +13 -0
- package/src/icons/react/FileCounter.tsx +13 -0
- package/src/icons/react/FileMultiple.tsx +13 -0
- package/src/icons/react/FileOther.tsx +13 -0
- package/src/icons/react/NoBorder.tsx +13 -0
- package/src/icons/react/NoFill.tsx +13 -0
- package/src/icons/react/ValueEdit.tsx +13 -0
- package/src/icons/react/index.tsx +9 -0
- package/src/icons/svg/chat-bubble.svg +4 -0
- package/src/icons/svg/chat-float.svg +4 -0
- package/src/icons/svg/chat-sidebar.svg +4 -0
- package/src/icons/svg/file-counter.svg +3 -0
- package/src/icons/svg/file-multiple.svg +3 -0
- package/src/icons/svg/file-other.svg +3 -0
- package/src/icons/svg/no-border.svg +17 -0
- package/src/icons/svg/no-fill.svg +4 -0
- package/src/icons/svg/value-edit.svg +3 -0
- package/src/images-gallery/images-gallery.tsx +8 -6
- package/src/images-gallery/slide-buttons.tsx +4 -11
- package/src/is-iOS.ts +0 -1
- package/src/is-in-popup.ts +2 -1
- package/src/lists/actions-menu-row-surface.tsx +7 -7
- package/src/loading-sausage.tsx +2 -2
- package/src/media-query-utils.ts +1 -2
- package/src/mobile-keyboard-aware-popup.tsx +6 -5
- package/src/modal-menu/modal-menu-content.tsx +2 -2
- package/src/number-input/decimal.js +9 -7
- package/src/number-input/{number-inline-input-with-autosize.tsx → number-input-inline-with-autosize.tsx} +20 -41
- package/src/number-input/number-input-inline.tsx +118 -0
- package/src/number-input/{index.test.js → number-input.test.tsx} +38 -43
- package/src/number-input/number-input.tsx +63 -121
- package/src/number-input/types.ts +19 -0
- package/src/number-input/utils.ts +61 -0
- package/src/online-users.tsx +2 -2
- package/src/palette-generator.test.ts +309 -0
- package/src/palette-generator.ts +160 -0
- package/src/palettes/_.ts +72 -0
- package/src/palettes/slate-arch.colors-snapshot.test.ts +1689 -0
- package/src/palettes/slate-arch.ts +188 -0
- package/src/palettes/slate-user.colors-snapshot.test.ts +1689 -0
- package/src/palettes/slate-user.ts +187 -0
- package/src/palettes/warm-arch.colors-snapshot.test.ts +1689 -0
- package/src/palettes/warm-arch.ts +222 -0
- package/src/palettes/warm-user.colors-snapshot.test.ts +1689 -0
- package/src/palettes/warm-user.ts +222 -0
- package/src/platform.ts +0 -3
- package/src/popover/get-element-ref.ts +28 -0
- package/src/popover/index.tsx +236 -339
- package/src/popover/{mobile-popup-context.tsx → mobile-popover-context.tsx} +7 -7
- package/src/popover/mobile-popover.tsx +169 -0
- package/src/popover/modifiers.tsx +2 -2
- package/src/popover/popup-stack-context.tsx +8 -9
- package/src/progress.tsx +2 -2
- package/src/reactions/reaction-button.tsx +12 -6
- package/src/root-theme-provider.test.tsx +411 -0
- package/src/scale-generator.ts +356 -0
- package/src/select/components/menu-list-virtualized.tsx +12 -25
- package/src/select/components/menu.tsx +12 -2
- package/src/select/index.tsx +6 -5
- package/src/select/select.tsx +38 -39
- package/src/select/styles.ts +0 -1
- package/src/select/util.ts +1 -1
- package/src/static-palettes.ts +356 -0
- package/src/thematic-color-picker.tsx +266 -0
- package/src/thematic-constants.tsx +27 -0
- package/src/thematic-controls.tsx +144 -0
- package/src/thematic-scales.tsx +122 -0
- package/src/thematic-state.ts +370 -0
- package/src/thematic.tsx +386 -0
- package/src/theme-provider.test.tsx +820 -0
- package/src/theme-provider.tsx +158 -92
- package/src/theme-settings.ts +67 -12
- package/src/theme-styles.ts +58 -6
- package/src/toast/toast-action.tsx +1 -1
- package/src/toast/toast.tsx +1 -2
- package/src/toggle-on-off.tsx +2 -2
- package/src/toggle.tsx +7 -8
- package/src/tooltip.tsx +14 -10
- package/src/type-badge.tsx +7 -14
- package/src/unit/styles.ts +2 -25
- package/src/unit/unit-with-tooltip.tsx +3 -2
- package/src/use-is-phone.tsx +7 -2
- package/src/use-long-press.tsx +2 -2
- package/src/use-on-screen-keyboard-data.tsx +2 -2
- package/src/with-data.tsx +4 -3
- package/src/workflow-progress-icon.tsx +2 -2
- package/.eslintignore +0 -3
- package/.eslintrc +0 -14
- package/src/__mocks__/createInlineTheme.js +0 -3
- package/src/create-inline-theme.ts +0 -66
- package/src/number-input/index.js +0 -191
- package/src/palette.ts +0 -237
package/src/theme-provider.tsx
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import invariant from "invariant";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
chooseGlobalStyles,
|
|
4
|
+
defaultLightTheme,
|
|
5
|
+
defaultLightThemeStyles,
|
|
6
|
+
defaultPalette,
|
|
7
|
+
selectGlobalTheme,
|
|
8
|
+
} from "./theme-styles";
|
|
3
9
|
import {layoutStyles} from "./layout-styles";
|
|
4
|
-
import {
|
|
5
|
-
|
|
10
|
+
import {
|
|
11
|
+
createContext,
|
|
12
|
+
forwardRef,
|
|
13
|
+
PropsWithChildren,
|
|
14
|
+
ReactNode,
|
|
15
|
+
useContext,
|
|
16
|
+
useEffect,
|
|
17
|
+
useLayoutEffect,
|
|
18
|
+
useMemo,
|
|
19
|
+
useState,
|
|
20
|
+
} from "react";
|
|
21
|
+
import {createInlineStyles, getThemeColors, ThemeColors, ThemeStyles} from "./design-system";
|
|
22
|
+
import {selectPalette, ThemePalette} from "./palette-generator";
|
|
6
23
|
import {
|
|
7
24
|
subscribeOnThemeMenuPreferenceChange,
|
|
8
25
|
subscribeOnThemeModeChange,
|
|
@@ -10,19 +27,30 @@ import {
|
|
|
10
27
|
ThemeMenuPreference,
|
|
11
28
|
ThemeMode,
|
|
12
29
|
ThemePreference,
|
|
30
|
+
useWarmPreference,
|
|
13
31
|
} from "./theme-settings";
|
|
32
|
+
import {css} from "@linaria/core";
|
|
14
33
|
|
|
15
|
-
const
|
|
16
|
-
|
|
34
|
+
const ThemePreferenceContext = createContext<ThemePreference | null>(null);
|
|
35
|
+
|
|
36
|
+
export function useThemePreference(): ThemePreference {
|
|
37
|
+
const themePreference = useContext(ThemePreferenceContext);
|
|
38
|
+
invariant(themePreference, `please ensure the component is wrapped in a <RootThemeProvider>`);
|
|
39
|
+
return themePreference;
|
|
40
|
+
}
|
|
17
41
|
|
|
18
|
-
const
|
|
42
|
+
const ThemeMenuPreferenceContext = createContext<ThemeMenuPreference | null>(null);
|
|
19
43
|
|
|
20
|
-
function
|
|
21
|
-
|
|
44
|
+
export function useThemeMenuPreference(): ThemeMenuPreference {
|
|
45
|
+
const themeMenuPreference = useContext(ThemeMenuPreferenceContext);
|
|
46
|
+
invariant(themeMenuPreference, `please ensure the component is wrapped in a <RootThemeProvider>`);
|
|
47
|
+
return themeMenuPreference;
|
|
22
48
|
}
|
|
23
49
|
|
|
50
|
+
const ThemeColorsContext = createContext<ThemeColors>(defaultLightTheme);
|
|
51
|
+
|
|
24
52
|
export function useTheme(): ThemeColors {
|
|
25
|
-
return useContext(
|
|
53
|
+
return useContext(ThemeColorsContext);
|
|
26
54
|
}
|
|
27
55
|
|
|
28
56
|
export function useThemeMode(): ThemeMode {
|
|
@@ -30,63 +58,153 @@ export function useThemeMode(): ThemeMode {
|
|
|
30
58
|
return theme.mode;
|
|
31
59
|
}
|
|
32
60
|
|
|
33
|
-
|
|
34
|
-
|
|
61
|
+
const RootThemeStylesContext = createContext<ThemeStyles>(defaultLightThemeStyles);
|
|
62
|
+
const ThemeStylesContext = createContext<ThemeStyles>(defaultLightThemeStyles);
|
|
35
63
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
);
|
|
64
|
+
export function useThemeStyles(): ThemeStyles {
|
|
65
|
+
return useContext(ThemeStylesContext);
|
|
66
|
+
}
|
|
40
67
|
|
|
41
|
-
|
|
68
|
+
const ThemePaletteContext = createContext<ThemePalette>(defaultPalette);
|
|
69
|
+
|
|
70
|
+
export function useThemePalette(): ThemePalette {
|
|
71
|
+
return useContext(ThemePaletteContext);
|
|
42
72
|
}
|
|
43
73
|
|
|
44
|
-
|
|
45
|
-
|
|
74
|
+
type ThematicContextValue = {
|
|
75
|
+
palette: ThemePalette;
|
|
76
|
+
setPalette: (palette: ThemePalette | null) => void;
|
|
77
|
+
};
|
|
46
78
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
79
|
+
const ThematicContext = createContext<ThematicContextValue>({
|
|
80
|
+
palette: defaultPalette,
|
|
81
|
+
setPalette: () => {},
|
|
82
|
+
});
|
|
51
83
|
|
|
52
|
-
|
|
84
|
+
export function useThematic(): ThematicContextValue {
|
|
85
|
+
return useContext(ThematicContext);
|
|
53
86
|
}
|
|
54
87
|
|
|
55
|
-
export function
|
|
56
|
-
|
|
57
|
-
|
|
88
|
+
export function ThemeProvider({
|
|
89
|
+
theme: themeOverride,
|
|
90
|
+
color: colorOverride,
|
|
91
|
+
mode: modeOverride,
|
|
92
|
+
portal,
|
|
93
|
+
children,
|
|
94
|
+
}: PropsWithChildren<
|
|
95
|
+
{portal?: true} & (
|
|
96
|
+
| {theme: ThemeColors; color?: never; mode?: never}
|
|
97
|
+
| {color?: string; theme?: never; mode?: ThemeMode}
|
|
98
|
+
)
|
|
99
|
+
>) {
|
|
100
|
+
const palette = useContext(ThemePaletteContext);
|
|
101
|
+
const parentTheme = useContext(ThemeColorsContext);
|
|
102
|
+
|
|
103
|
+
const mode = modeOverride ?? parentTheme.mode;
|
|
104
|
+
const color = colorOverride ?? parentTheme.primary;
|
|
105
|
+
const theme = themeOverride ?? getThemeColors(color, mode, palette);
|
|
106
|
+
|
|
107
|
+
const parentStyles = useContext(portal ? RootThemeStylesContext : ThemeStylesContext);
|
|
108
|
+
const styles = createInlineStyles(theme);
|
|
109
|
+
const stylesDiff = useMemo(() => {
|
|
110
|
+
if (styles === parentStyles) {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
const diff: ThemeStyles = {};
|
|
114
|
+
for (const key of Object.keys(styles)) {
|
|
115
|
+
if (styles[key] !== parentStyles[key]) {
|
|
116
|
+
diff[key] = styles[key];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return diff;
|
|
120
|
+
}, [styles, parentStyles]);
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<ThemeColorsContext.Provider value={theme}>
|
|
124
|
+
<ThemeStylesContext.Provider value={styles}>
|
|
125
|
+
<div
|
|
126
|
+
style={stylesDiff}
|
|
127
|
+
className={css`
|
|
128
|
+
display: contents;
|
|
129
|
+
`}
|
|
130
|
+
>
|
|
131
|
+
{children}
|
|
132
|
+
</div>
|
|
133
|
+
</ThemeStylesContext.Provider>
|
|
134
|
+
</ThemeColorsContext.Provider>
|
|
135
|
+
);
|
|
58
136
|
}
|
|
59
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Wrapper for ThemeProvider that absorbs Radix Portal's `asChild` ref/props.
|
|
140
|
+
* Use as direct child of Radix Portal components (MenuPortal, TooltipPortal, etc.)
|
|
141
|
+
*/
|
|
142
|
+
export const RadixPortalThemeProvider = forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<"div">>(
|
|
143
|
+
function PortalThemeProvider({children, ...rest}, ref) {
|
|
144
|
+
return (
|
|
145
|
+
<div ref={ref} {...rest}>
|
|
146
|
+
<ThemeProvider portal>{children}</ThemeProvider>
|
|
147
|
+
</div>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
|
|
60
152
|
export function MenuThemeProvider({children}: {children: ReactNode}) {
|
|
61
|
-
const
|
|
62
|
-
|
|
153
|
+
const themeMode = useThemeMode();
|
|
154
|
+
// light2 = always dark menu
|
|
155
|
+
const mode = themeMode === "light" ? "light" : "dark";
|
|
156
|
+
return <ThemeProvider mode={mode}>{children}</ThemeProvider>;
|
|
63
157
|
}
|
|
64
158
|
|
|
65
159
|
export function RootThemeProvider({
|
|
66
160
|
initialThemeMode,
|
|
67
161
|
initialPreference,
|
|
68
162
|
initialMenuPreference,
|
|
163
|
+
isArchitect = false,
|
|
69
164
|
children,
|
|
70
165
|
}: {
|
|
71
166
|
initialThemeMode: ThemeMode;
|
|
72
167
|
initialPreference: ThemePreference;
|
|
73
168
|
initialMenuPreference: ThemeMenuPreference;
|
|
169
|
+
isArchitect?: boolean;
|
|
74
170
|
children: ReactNode;
|
|
75
171
|
}): JSX.Element {
|
|
76
172
|
const [themeMode, setThemeMode] = useState<ThemeMode>(initialThemeMode);
|
|
173
|
+
useEffect(() => subscribeOnThemeModeChange(setThemeMode), []);
|
|
174
|
+
|
|
175
|
+
const [warm] = useWarmPreference();
|
|
176
|
+
useEffect(() => chooseGlobalStyles(themeMode, warm), [themeMode, warm]);
|
|
177
|
+
|
|
178
|
+
const [paletteOverride, setPaletteOverride] = useState<ThemePalette | null>(null);
|
|
179
|
+
|
|
180
|
+
const palette = useMemo(() => {
|
|
181
|
+
return paletteOverride ?? selectPalette({warmLook: warm, isArchitect});
|
|
182
|
+
}, [paletteOverride, warm, isArchitect]);
|
|
183
|
+
|
|
184
|
+
let res = <ThemeProvider theme={getThemeColors(null, themeMode, palette)}>{children}</ThemeProvider>;
|
|
185
|
+
|
|
186
|
+
// Set base context to what global CSS has, so ThemeProvider diffs against it
|
|
187
|
+
const [cssThemeColors, cssThemeStyles] = selectGlobalTheme(warm, themeMode === "dark");
|
|
188
|
+
res = <ThemeStylesContext.Provider value={cssThemeStyles}>{res}</ThemeStylesContext.Provider>;
|
|
189
|
+
res = <RootThemeStylesContext.Provider value={cssThemeStyles}>{res}</RootThemeStylesContext.Provider>;
|
|
190
|
+
res = <ThemeColorsContext.Provider value={cssThemeColors}>{res}</ThemeColorsContext.Provider>;
|
|
191
|
+
|
|
192
|
+
res = <ThemePaletteContext.Provider value={palette}>{res}</ThemePaletteContext.Provider>;
|
|
193
|
+
|
|
194
|
+
const thematicContextValue = useMemo<ThematicContextValue>(
|
|
195
|
+
() => ({palette, setPalette: setPaletteOverride}),
|
|
196
|
+
[palette, setPaletteOverride]
|
|
197
|
+
);
|
|
198
|
+
res = <ThematicContext.Provider value={thematicContextValue}>{res}</ThematicContext.Provider>;
|
|
199
|
+
|
|
77
200
|
const [themePreference, setThemePreference] = useState<ThemePreference>(initialPreference);
|
|
201
|
+
useEffect(() => subscribeOnThemePreferenceChange(setThemePreference), []);
|
|
202
|
+
res = <ThemePreferenceContext.Provider value={themePreference}>{res}</ThemePreferenceContext.Provider>;
|
|
203
|
+
|
|
78
204
|
const [themeMenuPreference, setThemeMenuPreference] = useState<ThemeMenuPreference>(initialMenuPreference);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const unsubscribePreference = subscribeOnThemePreferenceChange(setThemePreference);
|
|
83
|
-
const unsubscribeMenuPreference = subscribeOnThemeMenuPreferenceChange(setThemeMenuPreference);
|
|
84
|
-
return () => {
|
|
85
|
-
unsubscribeMode();
|
|
86
|
-
unsubscribePreference();
|
|
87
|
-
unsubscribeMenuPreference();
|
|
88
|
-
};
|
|
89
|
-
}, []);
|
|
205
|
+
useEffect(() => subscribeOnThemeMenuPreferenceChange(setThemeMenuPreference), []);
|
|
206
|
+
res = <ThemeMenuPreferenceContext.Provider value={themeMenuPreference}>{res}</ThemeMenuPreferenceContext.Provider>;
|
|
207
|
+
|
|
90
208
|
useLayoutEffect(() => {
|
|
91
209
|
const documentElement = document.documentElement;
|
|
92
210
|
documentElement.classList.add(layoutStyles);
|
|
@@ -94,58 +212,6 @@ export function RootThemeProvider({
|
|
|
94
212
|
documentElement.classList.remove(layoutStyles);
|
|
95
213
|
};
|
|
96
214
|
}, []);
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
const darkThemeClassName = "dark-theme";
|
|
99
|
-
const lightThemeAndDarkMenuClassName = "light-theme-and-dark-menu";
|
|
100
|
-
const meta = document.querySelector('meta[name="color-scheme"]');
|
|
101
|
-
let colorScheme = "light";
|
|
102
|
-
const documentElement = document.documentElement;
|
|
103
|
-
documentElement.classList.remove(darkThemeClassName);
|
|
104
|
-
documentElement.classList.remove(lightThemeAndDarkMenuClassName);
|
|
105
|
-
documentElement.classList.add(themeStyles);
|
|
106
|
-
if (themeMode === "dark") {
|
|
107
|
-
documentElement.classList.add(darkThemeClassName);
|
|
108
|
-
colorScheme = "dark";
|
|
109
|
-
} else if (themeMode === "light2") {
|
|
110
|
-
documentElement.classList.add(lightThemeAndDarkMenuClassName);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
documentElement.style.colorScheme = colorScheme;
|
|
114
|
-
meta?.setAttribute("content", colorScheme);
|
|
115
215
|
|
|
116
|
-
|
|
117
|
-
documentElement.classList.remove(darkThemeClassName);
|
|
118
|
-
documentElement.classList.remove(lightThemeAndDarkMenuClassName);
|
|
119
|
-
};
|
|
120
|
-
}, [themeMode]);
|
|
121
|
-
|
|
122
|
-
return (
|
|
123
|
-
<FiberyThemePreferenceContext.Provider value={themePreference}>
|
|
124
|
-
<FiberyThemeMenuPreferenceContext.Provider value={themeMenuPreference}>
|
|
125
|
-
<ThemeProvider theme={theme}>{children}</ThemeProvider>
|
|
126
|
-
</FiberyThemeMenuPreferenceContext.Provider>
|
|
127
|
-
</FiberyThemePreferenceContext.Provider>
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
interface WithThemeModeProps {
|
|
132
|
-
themeMode: ThemeMode;
|
|
216
|
+
return res;
|
|
133
217
|
}
|
|
134
|
-
|
|
135
|
-
export function withThemeMode<T extends WithThemeModeProps = WithThemeModeProps>(
|
|
136
|
-
WrappedComponent: React.ComponentType<T>
|
|
137
|
-
) {
|
|
138
|
-
const displayName = WrappedComponent.displayName || WrappedComponent.name || "Component";
|
|
139
|
-
|
|
140
|
-
const ComponentWithThemeMode = (props: Omit<T, keyof WithThemeModeProps>) => {
|
|
141
|
-
const themeMode = useThemeMode();
|
|
142
|
-
|
|
143
|
-
return <WrappedComponent {...(props as T)} themeMode={themeMode} />;
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
ComponentWithThemeMode.displayName = `withThemeMode(${displayName})`;
|
|
147
|
-
|
|
148
|
-
return ComponentWithThemeMode;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export {ThemeProvider};
|
package/src/theme-settings.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {useEffect, useState} from "react";
|
|
1
2
|
import {getMediaQueryList, subscribeOnChange as subscribeOnMediaQueryChange} from "./media-query-utils";
|
|
2
3
|
|
|
3
4
|
const listenersThemeMode = new Map();
|
|
@@ -10,6 +11,7 @@ const themeMenuPreferenceKey = "theme-menu-preference";
|
|
|
10
11
|
export type ThemePreference = "dark" | "light" | "auto";
|
|
11
12
|
export type ThemeMenuPreference = "dark" | "auto";
|
|
12
13
|
|
|
14
|
+
export const themeModes = ["dark", "light", "light2"] as const;
|
|
13
15
|
export type ThemeMode = "dark" | "light" | "light2";
|
|
14
16
|
|
|
15
17
|
export function getThemePreference(): ThemePreference {
|
|
@@ -42,7 +44,7 @@ export function getThemeMenuPreference(): ThemeMenuPreference {
|
|
|
42
44
|
return "auto";
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
export const alignTheme = (theme: ThemeMode) => {
|
|
47
|
+
export const alignTheme = (theme: ThemeMode): "light" | "dark" => {
|
|
46
48
|
if (theme === "light2") {
|
|
47
49
|
return "light";
|
|
48
50
|
}
|
|
@@ -56,22 +58,18 @@ function getDarkMediaQuery() {
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
export function getThemeMode(): ThemeMode {
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const matches = getDarkMediaQuery().matches;
|
|
64
|
-
if (matches) {
|
|
61
|
+
const item = getThemePreference();
|
|
62
|
+
const menu = getThemeMenuPreference();
|
|
63
|
+
if (item === "auto") {
|
|
64
|
+
if (getDarkMediaQuery().matches) {
|
|
65
65
|
return "dark";
|
|
66
|
-
}
|
|
67
|
-
if (menuPreference === "dark") {
|
|
66
|
+
} else if (menu === "dark") {
|
|
68
67
|
return "light2";
|
|
69
68
|
}
|
|
70
69
|
return "light";
|
|
71
|
-
} else if (
|
|
70
|
+
} else if (item === "dark") {
|
|
72
71
|
return "dark";
|
|
73
|
-
}
|
|
74
|
-
if (menuPreference === "dark") {
|
|
72
|
+
} else if (menu === "dark") {
|
|
75
73
|
return "light2";
|
|
76
74
|
}
|
|
77
75
|
return "light";
|
|
@@ -171,3 +169,60 @@ export function subscribeOnThemeModeChange(cb: (themeMode: ThemeMode) => void):
|
|
|
171
169
|
window.removeEventListener("storage", storageListener);
|
|
172
170
|
};
|
|
173
171
|
}
|
|
172
|
+
|
|
173
|
+
const warmKey = "warm";
|
|
174
|
+
const warmListeners = new Set<(warm: boolean) => void>();
|
|
175
|
+
|
|
176
|
+
export function hasWarmPreference(): boolean {
|
|
177
|
+
try {
|
|
178
|
+
return localStorage.getItem(warmKey) !== null;
|
|
179
|
+
} catch {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function loadWarmPreference(): boolean {
|
|
185
|
+
try {
|
|
186
|
+
return localStorage.getItem(warmKey) === "true";
|
|
187
|
+
} catch {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function storeWarmPreference(warm: boolean) {
|
|
193
|
+
try {
|
|
194
|
+
localStorage.setItem(warmKey, JSON.stringify(warm));
|
|
195
|
+
} catch {
|
|
196
|
+
// ignore
|
|
197
|
+
}
|
|
198
|
+
warmListeners.forEach((cb) => cb(warm));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function subscribeOnWarmPreferenceChange(cb: (warm: boolean) => void): () => void {
|
|
202
|
+
warmListeners.add(cb);
|
|
203
|
+
let prev = loadWarmPreference();
|
|
204
|
+
const storageListener = () => {
|
|
205
|
+
const next = loadWarmPreference();
|
|
206
|
+
if (prev !== next) {
|
|
207
|
+
prev = next;
|
|
208
|
+
cb(next);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
window.addEventListener("storage", storageListener);
|
|
212
|
+
return () => {
|
|
213
|
+
warmListeners.delete(cb);
|
|
214
|
+
window.removeEventListener("storage", storageListener);
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function useWarmPreference() {
|
|
219
|
+
const [warm, setWarm] = useState(loadWarmPreference);
|
|
220
|
+
useEffect(() => subscribeOnWarmPreferenceChange(setWarm), []);
|
|
221
|
+
return [
|
|
222
|
+
warm,
|
|
223
|
+
(warm: boolean) => {
|
|
224
|
+
setWarm(warm);
|
|
225
|
+
storeWarmPreference(warm);
|
|
226
|
+
},
|
|
227
|
+
] as const;
|
|
228
|
+
}
|
package/src/theme-styles.ts
CHANGED
|
@@ -1,14 +1,66 @@
|
|
|
1
1
|
import {css} from "@linaria/core";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import {createInlineStyles, getThemeColors, ThemeColors, ThemeStyles} from "./design-system";
|
|
3
|
+
import {warmUserPalette} from "./palettes/warm-user";
|
|
4
|
+
import {slateUserPalette} from "./palettes/slate-user";
|
|
5
|
+
import {ThemeMode} from "./theme-settings";
|
|
5
6
|
|
|
6
|
-
export const
|
|
7
|
+
export const defaultPalette = slateUserPalette;
|
|
8
|
+
export const defaultLightTheme = getThemeColors(null, "light", defaultPalette);
|
|
9
|
+
export const defaultDarkTheme = getThemeColors(null, "dark", defaultPalette);
|
|
10
|
+
|
|
11
|
+
const warmPalette = warmUserPalette;
|
|
12
|
+
const warmLightTheme = getThemeColors(null, "light", warmPalette);
|
|
13
|
+
const warmDarkTheme = getThemeColors(null, "dark", warmPalette);
|
|
14
|
+
|
|
15
|
+
export const defaultLightThemeStyles = createInlineStyles(defaultLightTheme);
|
|
16
|
+
export const defaultDarkThemeStyles = createInlineStyles(defaultDarkTheme);
|
|
17
|
+
|
|
18
|
+
const warmLightThemeStyles = createInlineStyles(warmLightTheme);
|
|
19
|
+
const warmDarkThemeStyles = createInlineStyles(warmDarkTheme);
|
|
20
|
+
|
|
21
|
+
export function selectGlobalTheme(warm: boolean, dark: boolean): [colors: ThemeColors, styles: ThemeStyles] {
|
|
22
|
+
if (warm) {
|
|
23
|
+
return dark ? [warmDarkTheme, warmDarkThemeStyles] : [warmLightTheme, warmLightThemeStyles];
|
|
24
|
+
} else {
|
|
25
|
+
return dark ? [defaultDarkTheme, defaultDarkThemeStyles] : [defaultLightTheme, defaultLightThemeStyles];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const darkClassName = "dark-theme";
|
|
30
|
+
const light2ClassName = "light-theme-and-dark-menu";
|
|
31
|
+
const warmClassName = "warm-palette";
|
|
32
|
+
|
|
33
|
+
export function chooseGlobalStyles(themeMode: ThemeMode, warm: boolean) {
|
|
34
|
+
const htmlElement = document.documentElement;
|
|
35
|
+
htmlElement.classList.toggle(darkClassName, themeMode === "dark");
|
|
36
|
+
htmlElement.classList.toggle(light2ClassName, themeMode === "light2");
|
|
37
|
+
htmlElement.classList.toggle(warmClassName, warm);
|
|
38
|
+
|
|
39
|
+
const colorScheme = themeMode === "dark" ? "dark" : "light";
|
|
40
|
+
htmlElement.style.colorScheme = colorScheme;
|
|
41
|
+
|
|
42
|
+
// Multiple color schemes (FIXME: missing, unknown implications) "light" | "dark" | "light dark" | "dark light"
|
|
43
|
+
// indicates that the first scheme is preferred by the document, but that the second scheme is acceptable if the user prefers it.
|
|
44
|
+
// see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/meta/name/color-scheme#usage_notes
|
|
45
|
+
const colorSchemeMeta = document.querySelector('meta[name="color-scheme"]');
|
|
46
|
+
colorSchemeMeta?.setAttribute("content", colorScheme);
|
|
47
|
+
|
|
48
|
+
htmlElement.classList.add(globals);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const globals = css`
|
|
7
52
|
:global() {
|
|
8
53
|
html:root {
|
|
9
|
-
${
|
|
54
|
+
${defaultLightThemeStyles}
|
|
10
55
|
&.dark-theme {
|
|
11
|
-
${
|
|
56
|
+
${defaultDarkThemeStyles}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
&.warm-palette {
|
|
60
|
+
${warmLightThemeStyles}
|
|
61
|
+
&.dark-theme {
|
|
62
|
+
${warmDarkThemeStyles}
|
|
63
|
+
}
|
|
12
64
|
}
|
|
13
65
|
}
|
|
14
66
|
}
|
|
@@ -19,7 +19,7 @@ export const ToastAction = forwardRef<
|
|
|
19
19
|
ButtonProps & {altText: ToastActionProps["altText"]}
|
|
20
20
|
>(({children, altText, ...props}, ref) => {
|
|
21
21
|
const theme = useTheme();
|
|
22
|
-
const buttonColors = theme.mode === "dark" ? makeButtonColors(theme.textColor, "outline") : {};
|
|
22
|
+
const buttonColors = theme.mode === "dark" ? makeButtonColors(theme.textColor, "outline", theme) : {};
|
|
23
23
|
return (
|
|
24
24
|
<RadixToastAction className={toastActionCss} ref={ref} altText={altText} asChild>
|
|
25
25
|
<Button style={buttonColors} variant="outline" color="neutral" {...props}>
|
package/src/toast/toast.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {useCallbackRef} from "@fibery/react/src/use-callback-ref";
|
|
2
2
|
import {useControllableState} from "@fibery/react/src/use-controllable-state";
|
|
3
3
|
import {css, cx} from "@linaria/core";
|
|
4
|
-
import {useEffect, useRef
|
|
4
|
+
import {useCallback, useEffect, useRef} from "react";
|
|
5
5
|
import {space, themeVars} from "../design-system";
|
|
6
6
|
import Spinner from "../icons/react/Spinner";
|
|
7
7
|
import CheckCircleFilled from "../icons/react/CheckCircleFilled";
|
|
@@ -115,7 +115,6 @@ export const Toast: React.FC<ToastProps> = (props) => {
|
|
|
115
115
|
clearTimeout(emergencyTimeout);
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
119
118
|
return () => {};
|
|
120
119
|
}, [isOpen, onHideCb]);
|
|
121
120
|
|
package/src/toggle-on-off.tsx
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import chroma from "chroma-js";
|
|
2
1
|
import {FC, ComponentProps, useMemo} from "react";
|
|
2
|
+
import {isValidColor} from "./color-utils";
|
|
3
3
|
import {ThemeColors} from "./design-system";
|
|
4
4
|
import {useTheme} from "./theme-provider";
|
|
5
5
|
import {Toggle} from "./toggle";
|
|
6
6
|
import {useIsPhone} from "./use-is-phone";
|
|
7
7
|
|
|
8
8
|
const getBackgroundColors = (accentColor: string | undefined, theme: ThemeColors) => {
|
|
9
|
-
return accentColor &&
|
|
9
|
+
return accentColor && isValidColor(accentColor)
|
|
10
10
|
? {
|
|
11
11
|
enabledColor: accentColor,
|
|
12
12
|
disabledColor: theme.colorBgButtonSoftNeutralHover,
|
package/src/toggle.tsx
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {css, cx} from "@linaria/core";
|
|
2
2
|
import {styled} from "@linaria/react";
|
|
3
|
-
import chroma from "chroma-js";
|
|
4
3
|
import {ComponentProps, FC, ReactNode} from "react";
|
|
5
4
|
import {space, textStyles, transition} from "./design-system";
|
|
6
|
-
import
|
|
5
|
+
import memoize from "lodash/memoize";
|
|
7
6
|
import SpinnerIcon from "./icons/react/Spinner";
|
|
8
7
|
import {mobileRootSelector} from "./mobile-styles";
|
|
8
|
+
import {darkenChroma, getAlpha, produceColor, setAlpha} from "./color-utils";
|
|
9
9
|
|
|
10
10
|
const toggleIconHeightVar = "--fibery-toggle-icon-height";
|
|
11
11
|
const toggleIconWidthVar = "--fibery-toggle-icon-width";
|
|
@@ -48,14 +48,12 @@ const labelWrapperStyle = css`
|
|
|
48
48
|
align-items: center;
|
|
49
49
|
`;
|
|
50
50
|
|
|
51
|
-
const getOutlineColor =
|
|
52
|
-
const alpha =
|
|
51
|
+
const getOutlineColor = memoize((color: string) => {
|
|
52
|
+
const alpha = getAlpha(color);
|
|
53
53
|
if (alpha < 1) {
|
|
54
|
-
return
|
|
55
|
-
.alpha(alpha + 0.3)
|
|
56
|
-
.css();
|
|
54
|
+
return produceColor(color, (c) => setAlpha(c, Math.min(1, alpha + 0.3)));
|
|
57
55
|
} else {
|
|
58
|
-
return
|
|
56
|
+
return produceColor(color, (c) => darkenChroma(c, 1));
|
|
59
57
|
}
|
|
60
58
|
});
|
|
61
59
|
|
|
@@ -124,6 +122,7 @@ type ToggleProps = Omit<ComponentProps<"input">, "value" | "size"> & {
|
|
|
124
122
|
size?: ToggleSize;
|
|
125
123
|
};
|
|
126
124
|
|
|
125
|
+
/** @deprecated implementation detail */
|
|
127
126
|
export const Toggle: FC<ToggleProps> = ({
|
|
128
127
|
value,
|
|
129
128
|
label,
|
package/src/tooltip.tsx
CHANGED
|
@@ -2,7 +2,9 @@ import {css, cx} from "@linaria/core";
|
|
|
2
2
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
3
3
|
import {forwardRef, type ReactNode, type SyntheticEvent} from "react";
|
|
4
4
|
import {border, fontWeight, lineHeight, space, themeVars, tooltipDelay} from "./design-system";
|
|
5
|
+
import {RadixPortalThemeProvider} from "./theme-provider";
|
|
5
6
|
|
|
7
|
+
// FIXME consolidate as common util
|
|
6
8
|
const isFlagPresent = (flag: string) => new RegExp(`[?&]${flag}\\b`).test(window.location.search);
|
|
7
9
|
|
|
8
10
|
const preventDefaultAndStopPropagation = (e: SyntheticEvent) => {
|
|
@@ -204,16 +206,18 @@ export const Tooltip = forwardRef<HTMLButtonElement, TooltipProps>(
|
|
|
204
206
|
{children}
|
|
205
207
|
</TooltipPrimitive.Trigger>
|
|
206
208
|
<TooltipPrimitive.Portal container={container}>
|
|
207
|
-
<
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
209
|
+
<RadixPortalThemeProvider>
|
|
210
|
+
<TooltipPrimitive.Content
|
|
211
|
+
ref={setContentElement}
|
|
212
|
+
side={side}
|
|
213
|
+
sideOffset={sideOffset}
|
|
214
|
+
align={align}
|
|
215
|
+
alignOffset={alignOffset}
|
|
216
|
+
className={cx(tooltipStyle, whiteBg && whiteBgStyle, tooltipContentStyle)}
|
|
217
|
+
>
|
|
218
|
+
{content}
|
|
219
|
+
</TooltipPrimitive.Content>
|
|
220
|
+
</RadixPortalThemeProvider>
|
|
217
221
|
</TooltipPrimitive.Portal>
|
|
218
222
|
</TooltipPrimitive.Root>
|
|
219
223
|
);
|
package/src/type-badge.tsx
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import {css} from "@linaria/core";
|
|
2
2
|
import cx from "classnames";
|
|
3
|
-
import
|
|
3
|
+
import isString from "lodash/isString";
|
|
4
4
|
import {CSSProperties, forwardRef, memo, ReactNode} from "react";
|
|
5
5
|
import {a11yColor} from "./a11y-color";
|
|
6
6
|
import {
|
|
7
7
|
border,
|
|
8
|
-
colors,
|
|
9
8
|
fontWeight,
|
|
10
9
|
getDarkenColor,
|
|
11
10
|
getTextColor,
|
|
12
|
-
getThemeValue,
|
|
13
11
|
opacity,
|
|
14
12
|
space,
|
|
15
13
|
textStyles,
|
|
14
|
+
themeVars,
|
|
16
15
|
transition,
|
|
17
16
|
} from "./design-system";
|
|
18
17
|
import {useTheme} from "./theme-provider";
|
|
@@ -180,7 +179,7 @@ export const TypeBadge = memo(
|
|
|
180
179
|
) {
|
|
181
180
|
const TagName = onClick ? "button" : "div";
|
|
182
181
|
const title =
|
|
183
|
-
|
|
182
|
+
isString(children) && abbr ? (
|
|
184
183
|
noTooltip ? (
|
|
185
184
|
makeAbbreviation(children)
|
|
186
185
|
) : (
|
|
@@ -196,13 +195,9 @@ export const TypeBadge = memo(
|
|
|
196
195
|
|
|
197
196
|
const baseColor = color || theme.iconColor;
|
|
198
197
|
|
|
199
|
-
const textBadgeColor =
|
|
200
|
-
dark: a11yColor(getTextColor(baseColor), getDarkenColor(baseColor), 12),
|
|
201
|
-
light: a11yColor(getTextColor(baseColor), getDarkenColor(baseColor), 12),
|
|
202
|
-
light2: a11yColor(getTextColor(baseColor), getDarkenColor(baseColor), 12),
|
|
203
|
-
});
|
|
198
|
+
const textBadgeColor = a11yColor(getTextColor(baseColor), getDarkenColor(baseColor), 12);
|
|
204
199
|
|
|
205
|
-
const backgroundBadgeColor =
|
|
200
|
+
const backgroundBadgeColor = theme.select({
|
|
206
201
|
dark: getDarkenColor(baseColor),
|
|
207
202
|
light: baseColor,
|
|
208
203
|
light2: baseColor,
|
|
@@ -210,10 +205,8 @@ export const TypeBadge = memo(
|
|
|
210
205
|
|
|
211
206
|
const resultingStyle = {
|
|
212
207
|
...style,
|
|
213
|
-
[colorProp]: baseColor ? textBadgeColor :
|
|
214
|
-
|
|
215
|
-
[colorBackgroundProp]: backgroundBadgeColor || colors.shades.opacity25,
|
|
216
|
-
//[colorBackgroundProp]: baseColor || colors.shades.opacity25,
|
|
208
|
+
[colorProp]: baseColor ? textBadgeColor : themeVars.inversedTextColor,
|
|
209
|
+
[colorBackgroundProp]: backgroundBadgeColor || themeVars.shades.opacity25,
|
|
217
210
|
[colorBorderProp]: backgroundBadgeColor,
|
|
218
211
|
[colorDarkProp]: getDarkenColor(baseColor),
|
|
219
212
|
} as CSSProperties;
|