@fibery/ui-kit 1.40.4 → 1.42.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.
Files changed (143) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/eslint.config.mjs +16 -0
  3. package/package.json +10 -9
  4. package/src/a11y-color.test.ts +5 -24
  5. package/src/actions-menu/actions-menu-item.tsx +6 -9
  6. package/src/actions-menu/context-actions-menu.tsx +8 -3
  7. package/src/ai/model.tsx +0 -1
  8. package/src/ai/temperature.tsx +0 -1
  9. package/src/animated-height-container.tsx +3 -3
  10. package/src/antd/index.tsx +3 -0
  11. package/src/antd/input.tsx +0 -1
  12. package/src/antd/styles.ts +9 -9
  13. package/src/app-icon-with-fallback.tsx +5 -5
  14. package/src/app-icon-wrapper.tsx +2 -3
  15. package/src/app-icon.tsx +3 -3
  16. package/src/avatar.tsx +3 -3
  17. package/src/button/base-button.tsx +0 -1
  18. package/src/button/make-button-colors.ts +2 -2
  19. package/src/checkbox.tsx +1 -1
  20. package/src/collapsible-section.tsx +11 -1
  21. package/src/color-utils.test.ts +15 -25
  22. package/src/color-utils.ts +43 -8
  23. package/src/command-menu/index.tsx +1 -0
  24. package/src/context-menu/index.tsx +5 -5
  25. package/src/date-picker/contexts.ts +2 -2
  26. package/src/date-picker/date-range-picker-popup-content.tsx +1 -2
  27. package/src/date-picker/date-range-picker.tsx +29 -31
  28. package/src/date-picker/single-date-picker-popup-content.tsx +1 -2
  29. package/src/date-picker/single-date-picker.tsx +149 -124
  30. package/src/date-picker/types.ts +4 -4
  31. package/src/day-select/iso-week-day-select.tsx +2 -2
  32. package/src/day-select/week-day-select.tsx +2 -2
  33. package/src/delayed.tsx +0 -1
  34. package/src/design-system/alpha.test.ts +59 -0
  35. package/src/design-system/alpha.ts +41 -0
  36. package/src/design-system/animation.ts +10 -0
  37. package/src/design-system/colors-css.test.ts +39 -0
  38. package/src/design-system/colors-css.ts +25 -0
  39. package/src/design-system/colors-js.test.ts +232 -0
  40. package/src/design-system/colors-js.ts +107 -0
  41. package/src/design-system/colors.test.ts +74 -0
  42. package/src/{design-system.colors.ts → design-system/colors.ts} +133 -134
  43. package/src/design-system/date.ts +3 -0
  44. package/src/design-system/layout.ts +106 -0
  45. package/src/design-system/theme.test.ts +94 -0
  46. package/src/design-system/theme.ts +93 -0
  47. package/src/design-system/typography.ts +179 -0
  48. package/src/design-system/vars.test.ts +1679 -0
  49. package/src/design-system/vars.ts +52 -0
  50. package/src/design-system.test.ts +46 -303
  51. package/src/design-system.ts +25 -545
  52. package/src/dropdown-menu/index.tsx +5 -5
  53. package/src/emoji-picker/icon-emoji-picker.tsx +1 -1
  54. package/src/fibermoji-placeholder.tsx +3 -3
  55. package/src/field-container.tsx +11 -2
  56. package/src/file-item/file-icon.tsx +3 -3
  57. package/src/file-item-2.tsx +4 -10
  58. package/src/file-item.tsx +0 -1
  59. package/src/icons/ast/ChatBubble.ts +8 -0
  60. package/src/icons/ast/ChatFloat.ts +8 -0
  61. package/src/icons/ast/ChatSidebar.ts +8 -0
  62. package/src/icons/ast/FileOther.ts +8 -0
  63. package/src/icons/ast/NoBorder.ts +8 -0
  64. package/src/icons/ast/NoFill.ts +8 -0
  65. package/src/icons/ast/index.tsx +6 -0
  66. package/src/icons/react/ChatBubble.tsx +13 -0
  67. package/src/icons/react/ChatFloat.tsx +13 -0
  68. package/src/icons/react/ChatSidebar.tsx +13 -0
  69. package/src/icons/react/FileOther.tsx +13 -0
  70. package/src/icons/react/NoBorder.tsx +13 -0
  71. package/src/icons/react/NoFill.tsx +13 -0
  72. package/src/icons/react/index.tsx +6 -0
  73. package/src/icons/svg/chat-bubble.svg +4 -0
  74. package/src/icons/svg/chat-float.svg +4 -0
  75. package/src/icons/svg/chat-sidebar.svg +4 -0
  76. package/src/icons/svg/file-other.svg +3 -0
  77. package/src/icons/svg/no-border.svg +17 -0
  78. package/src/icons/svg/no-fill.svg +4 -0
  79. package/src/images-gallery/images-gallery.tsx +8 -6
  80. package/src/images-gallery/slide-buttons.tsx +2 -2
  81. package/src/is-iOS.ts +0 -1
  82. package/src/is-in-popup.ts +2 -1
  83. package/src/lists/actions-menu-row-surface.tsx +4 -4
  84. package/src/loading-sausage.tsx +2 -2
  85. package/src/media-query-utils.ts +1 -2
  86. package/src/mobile-keyboard-aware-popup.tsx +2 -2
  87. package/src/modal-menu/modal-menu-content.tsx +2 -2
  88. package/src/number-input/decimal.js +9 -7
  89. package/src/number-input/{number-inline-input-with-autosize.tsx → number-input-inline-with-autosize.tsx} +20 -41
  90. package/src/number-input/number-input-inline.tsx +118 -0
  91. package/src/number-input/{index.test.js → number-input.test.tsx} +38 -43
  92. package/src/number-input/number-input.tsx +63 -121
  93. package/src/number-input/types.ts +19 -0
  94. package/src/number-input/utils.ts +61 -0
  95. package/src/online-users.tsx +2 -2
  96. package/src/palette-generator.test.ts +24 -281
  97. package/src/palette-generator.ts +39 -45
  98. package/src/palettes/_.ts +72 -0
  99. package/src/palettes/slate-arch.colors-snapshot.test.ts +1689 -0
  100. package/src/palettes/slate-arch.ts +188 -0
  101. package/src/palettes/slate-user.colors-snapshot.test.ts +1689 -0
  102. package/src/palettes/slate-user.ts +187 -0
  103. package/src/palettes/warm-arch.colors-snapshot.test.ts +1689 -0
  104. package/src/palettes/warm-arch.ts +222 -0
  105. package/src/palettes/warm-user.colors-snapshot.test.ts +1689 -0
  106. package/src/palettes/warm-user.ts +222 -0
  107. package/src/popover/get-element-ref.ts +28 -0
  108. package/src/popover/index.tsx +232 -333
  109. package/src/popover/{mobile-popup-context.tsx → mobile-popover-context.tsx} +7 -7
  110. package/src/popover/mobile-popover.tsx +169 -0
  111. package/src/popover/modifiers.tsx +2 -2
  112. package/src/popover/popup-stack-context.tsx +8 -9
  113. package/src/root-theme-provider.test.tsx +114 -19
  114. package/src/scale-generator.ts +31 -22
  115. package/src/select/components/menu-list-virtualized.tsx +5 -3
  116. package/src/select/index.tsx +6 -5
  117. package/src/select/select.tsx +36 -38
  118. package/src/select/util.ts +1 -1
  119. package/src/static-palettes.ts +221 -11
  120. package/src/thematic-controls.tsx +6 -6
  121. package/src/thematic-scales.tsx +15 -15
  122. package/src/thematic-state.ts +57 -20
  123. package/src/thematic.tsx +36 -32
  124. package/src/theme-provider.test.tsx +31 -19
  125. package/src/theme-provider.tsx +41 -38
  126. package/src/theme-settings.ts +66 -11
  127. package/src/theme-styles.ts +53 -8
  128. package/src/toast/toast.tsx +1 -2
  129. package/src/toggle.tsx +2 -2
  130. package/src/tooltip.tsx +4 -3
  131. package/src/type-badge.tsx +4 -11
  132. package/src/unit/styles.ts +0 -23
  133. package/src/use-is-phone.tsx +7 -2
  134. package/src/use-on-screen-keyboard-data.tsx +2 -2
  135. package/src/with-data.tsx +4 -3
  136. package/src/workflow-progress-icon.tsx +2 -2
  137. package/.eslintignore +0 -3
  138. package/.eslintrc +0 -14
  139. package/src/__mocks__/createInlineTheme.js +0 -3
  140. package/src/__snapshots__/design-system.test.ts.snap +0 -7265
  141. package/src/create-inline-theme.ts +0 -67
  142. package/src/number-input/index.js +0 -191
  143. package/src/palette.ts +0 -253
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @fibery/ui-kit
2
2
 
3
+ ## 1.42.0
4
+
5
+ ### Minor Changes
6
+
7
+ - c465007: Add typedObjectEntries into fibery/helpers
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [c465007]
12
+ - @fibery/helpers@1.5.0
13
+ - @fibery/react@1.5.0
14
+
15
+ ## 1.41.0
16
+
17
+ ### Minor Changes
18
+
19
+ - 6139f68: ThemePalette, design-system restructuring
20
+
3
21
  ## 1.40.4
4
22
 
5
23
  ### Patch Changes
@@ -0,0 +1,16 @@
1
+ import baseConfig from "@fibery/eslint-config";
2
+
3
+ export default [
4
+ ...baseConfig,
5
+ {
6
+ rules: {
7
+ "no-restricted-imports": ["error", {patterns: ["@fibery/ui-kit/*"]}],
8
+ },
9
+ },
10
+ {
11
+ files: ["src/icons/**/*"],
12
+ rules: {
13
+ "unicorn/filename-case": "off",
14
+ },
15
+ },
16
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fibery/ui-kit",
3
- "version": "1.40.4",
3
+ "version": "1.42.0",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "dependencies": {
@@ -10,6 +10,7 @@
10
10
  "@radix-ui/react-collapsible": "1.1.0",
11
11
  "@radix-ui/react-context-menu": "2.2.1",
12
12
  "@radix-ui/react-dropdown-menu": "2.1.1",
13
+ "@radix-ui/react-focus-scope": "^1.1.8",
13
14
  "@radix-ui/react-navigation-menu": "1.2.1",
14
15
  "@radix-ui/react-slot": "1.2.4",
15
16
  "@radix-ui/react-toast": "1.2.2",
@@ -20,7 +21,7 @@
20
21
  "@types/react-select-country-list": "^2.2.3",
21
22
  "@types/setimmediate": "^1.0.4",
22
23
  "antd": "4.24.7",
23
- "chrono-node": "^2.7.5",
24
+ "chrono-node": "^2.9.0",
24
25
  "classnames": "2.3.1",
25
26
  "cmdk": "1.0.0",
26
27
  "color-hash": "1.0.3",
@@ -42,12 +43,12 @@
42
43
  "react-popper": "2.3.0",
43
44
  "react-select": "5.3.2",
44
45
  "react-select-country-list": "2.2.1",
45
- "react-virtuoso": "4.14.1",
46
+ "react-virtuoso": "4.18.1",
46
47
  "screenfull": "6.0.2",
47
48
  "tabbable": "5.2.1",
48
49
  "@fibery/emoji-data": "2.7.1",
49
- "@fibery/helpers": "1.4.0",
50
- "@fibery/react": "1.4.7"
50
+ "@fibery/helpers": "1.5.0",
51
+ "@fibery/react": "1.5.0"
51
52
  },
52
53
  "peerDependencies": {
53
54
  "react": "18.3.1",
@@ -81,7 +82,7 @@
81
82
  "typescript": "5.9.3",
82
83
  "unist-util-reduce": "0.2.2",
83
84
  "@fibery/babel-preset": "7.4.1",
84
- "@fibery/eslint-config": "8.6.2"
85
+ "@fibery/eslint-config": "9.0.0"
85
86
  },
86
87
  "jest": {
87
88
  "testEnvironment": "jsdom",
@@ -97,8 +98,7 @@
97
98
  "cobertura"
98
99
  ],
99
100
  "moduleNameMapper": {
100
- "\\.(css)$": "<rootDir>/src/__mocks__/styleMock.js",
101
- "createInlineTheme\\.js$": "<rootDir>/src/__mocks__/createInlineTheme.js"
101
+ "\\.(css)$": "<rootDir>/src/__mocks__/styleMock.js"
102
102
  },
103
103
  "transform": {
104
104
  "^.+\\.(js|jsx|ts|tsx)$": [
@@ -129,6 +129,7 @@
129
129
  "test:ci": "pnpm run test --reporters=default --reporters=jest-junit",
130
130
  "test:coverage": "pnpm run test --coverage --coverageDirectory=${JEST_COVERAGE_RESULT_DIR:-$(pwd)}/coverage/ui-kit --reporters=default --reporters=jest-junit",
131
131
  "lint": "eslint .",
132
- "generate-icons": "node scripts/generate-icons.mjs"
132
+ "generate-icons": "node scripts/generate-icons.mjs",
133
+ "typecheck": "tsc --noEmit"
133
134
  }
134
135
  }
@@ -11,27 +11,27 @@ describe("a11yColor", () => {
11
11
  it("color already passes contrast on dark background", () => {
12
12
  // White on black should already pass
13
13
  const result = a11yColor("#ffffff", "#000000");
14
- expect(result).toBe("rgb(255,255,255)");
14
+ expect(result).toMatchInlineSnapshot(`"rgb(255,255,255)"`);
15
15
  });
16
16
 
17
17
  it("invalid toMakeA11y color is provided", () => {
18
18
  const result = a11yColor("invalid-color", "#ffffff");
19
- expect(result).toBe("invalid-color");
19
+ expect(result).toMatchInlineSnapshot(`"invalid-color"`);
20
20
  });
21
21
 
22
22
  it("invalid background color is provided", () => {
23
23
  const result = a11yColor("#ff0000", "invalid-bg");
24
- expect(result).toBe("#ff0000");
24
+ expect(result).toMatchInlineSnapshot(`"#ff0000"`);
25
25
  });
26
26
 
27
27
  it("transparent is provided as toMakeA11y", () => {
28
28
  const result = a11yColor("transparent", "#ffffff");
29
- expect(result).toBe("transparent");
29
+ expect(result).toMatchInlineSnapshot(`"transparent"`);
30
30
  });
31
31
 
32
32
  it("transparent is provided as background", () => {
33
33
  const result = a11yColor("#ff0000", "transparent");
34
- expect(result).toBe("#ff0000");
34
+ expect(result).toMatchInlineSnapshot(`"#ff0000"`);
35
35
  });
36
36
  });
37
37
 
@@ -40,16 +40,12 @@ describe("a11yColor", () => {
40
40
  // Light yellow on white needs to be darker
41
41
  const result = a11yColor("#ffff00", "#ffffff");
42
42
  expect(result).toMatchInlineSnapshot(`"rgb(154,154,0)"`);
43
- // Result should be different (darker) than input
44
- expect(result).not.toBe("rgb(255,255,0)");
45
43
  });
46
44
 
47
45
  it("makes dark color lighter on black background", () => {
48
46
  // Dark blue on black needs to be lighter
49
47
  const result = a11yColor("#000033", "#000000");
50
48
  expect(result).toMatchInlineSnapshot(`"rgb(49,49,255)"`);
51
- // Result should be different (lighter) than input
52
- expect(result).not.toBe("rgb(0,0,51)");
53
49
  });
54
50
 
55
51
  it("handles medium colors on light backgrounds", () => {
@@ -109,21 +105,6 @@ describe("a11yColor", () => {
109
105
  });
110
106
  });
111
107
 
112
- describe("caching", () => {
113
- it("returns same result for same inputs", () => {
114
- const result1 = a11yColor("#ff0000", "#ffffff");
115
- const result2 = a11yColor("#ff0000", "#ffffff");
116
- expect(result1).toBe(result2);
117
- });
118
-
119
- it("returns different results for different contrast ratios", () => {
120
- const result1 = a11yColor("#cccccc", "#ffffff", 3.0);
121
- const result2 = a11yColor("#cccccc", "#ffffff", 7.0);
122
- // Higher contrast requirement may produce different result
123
- expect(result1).not.toBe(result2);
124
- });
125
- });
126
-
127
108
  describe("real-world use cases", () => {
128
109
  it("brand green on white background", () => {
129
110
  const result = a11yColor("#69AC5E", "#ffffff");
@@ -12,7 +12,7 @@ import {useTheme} from "../theme-provider";
12
12
  import {useIsPhone} from "../use-is-phone";
13
13
  import {useActionsMenuContext} from "./contexts/actions-menu-context";
14
14
  import {useActiveDangerousRow, useSetActiveDangerousRow} from "./contexts/actions-menu-dangerous-rows";
15
- import _ from "lodash";
15
+ import noop from "lodash/noop";
16
16
 
17
17
  const hiddenTextCss = css`
18
18
  visibility: hidden;
@@ -55,18 +55,18 @@ export const dangerousItemCss = css`
55
55
  export const dangerousConfirmationCss = css`
56
56
  &.${rowCss} {
57
57
  background-color: ${themeVars.colorBgActionsMenuItemDangerActive};
58
- color: ${themeVars.inversedTextColor};
58
+ color: ${themeVars.whiteColor};
59
59
  border-radius: ${border.radius6}px;
60
60
 
61
- --actions-menu-item-icon-color: ${themeVars.inversedTextColor};
61
+ --actions-menu-item-icon-color: ${themeVars.whiteColor};
62
62
 
63
63
  &:hover,
64
64
  &[data-highlighted] {
65
65
  background-color: ${themeVars.colorBgActionsMenuItemDangerActive};
66
- color: ${themeVars.inversedTextColor};
66
+ color: ${themeVars.whiteColor};
67
67
  border-radius: ${border.radius6}px;
68
68
 
69
- --actions-menu-item-icon-color: ${themeVars.inversedTextColor};
69
+ --actions-menu-item-icon-color: ${themeVars.whiteColor};
70
70
  }
71
71
  }
72
72
  `;
@@ -95,7 +95,7 @@ const RightCorner = ({shortcut, loading, checked}: {shortcut?: string[]; loading
95
95
  const theme = useTheme();
96
96
  const isPhone = useIsPhone();
97
97
 
98
- const shortcuts = _.isArray(shortcut) ? shortcut : shortcut ? [shortcut] : null;
98
+ const shortcuts = Array.isArray(shortcut) ? shortcut : shortcut ? [shortcut] : null;
99
99
 
100
100
  return loading || shortcuts || checked ? (
101
101
  <div className={cornerCss}>
@@ -218,9 +218,6 @@ const ActionsMenuItemComponent: React.FC<
218
218
  );
219
219
  });
220
220
 
221
- // eslint-disable-next-line @typescript-eslint/no-empty-function
222
- const noop = () => {};
223
-
224
221
  export const ActionsMenuItem: React.FC<ActionsMenuItemProps> = ({className, ...props}) => {
225
222
  const id = useId();
226
223
  const activeDangerousRow = useActiveDangerousRow();
@@ -1,4 +1,4 @@
1
- import _ from "lodash";
1
+ import noop from "lodash/noop";
2
2
  import {useControllableState} from "@fibery/react/src/use-controllable-state";
3
3
  import {ActionsMenuContextProvider} from "./contexts/actions-menu-context";
4
4
  import * as ContextMenu from "../context-menu";
@@ -12,13 +12,14 @@ import {useState} from "react";
12
12
 
13
13
  export const ContextActionsMenu = ({
14
14
  open,
15
- onOpenChange = _.noop,
15
+ onOpenChange = noop,
16
16
  trigger,
17
17
  children,
18
18
  disabled,
19
19
  mobileTitle,
20
20
  supportsMobile,
21
21
  autoFocusOnClose = true,
22
+ contentStyles,
22
23
  }: ActionsMenuProps): JSX.Element => {
23
24
  const isPhone = useIsPhone();
24
25
  const [isOpen = false, setOpen] = useControllableState({value: open, onChange: onOpenChange});
@@ -31,6 +32,7 @@ export const ContextActionsMenu = ({
31
32
  open={open}
32
33
  onOpenChange={onOpenChange}
33
34
  mobileTitle={mobileTitle}
35
+ contentStyles={contentStyles}
34
36
  supportsMobile
35
37
  // Extra div is important here to not loose the original onClick handler
36
38
  trigger={<div>{trigger}</div>}
@@ -48,7 +50,10 @@ export const ContextActionsMenu = ({
48
50
  {trigger}
49
51
  </ContextMenu.Trigger>
50
52
  <ContextMenu.Portal>
51
- <ContextMenu.Content onCloseAutoFocus={autoFocusOnClose ? undefined : preventDefault}>
53
+ <ContextMenu.Content
54
+ className={contentStyles}
55
+ onCloseAutoFocus={autoFocusOnClose ? undefined : preventDefault}
56
+ >
52
57
  <ActionsMenuDangerousRowsProvider open={isOpen}>{children}</ActionsMenuDangerousRowsProvider>
53
58
  </ContextMenu.Content>
54
59
  </ContextMenu.Portal>
package/src/ai/model.tsx CHANGED
@@ -66,7 +66,6 @@ export const Model = ({initialValue, onChange}: Props) => {
66
66
  <Tooltip
67
67
  // props
68
68
  title={`Model: ${item.name}`}
69
- // eslint-disable-next-line
70
69
  description={item.desc}
71
70
  instant
72
71
  >
@@ -60,7 +60,6 @@ export const Temperature = ({initialValue, onChange}: Props) => {
60
60
  <Tooltip
61
61
  // props
62
62
  title={`Click to change`}
63
- // eslint-disable-next-line
64
63
  description={`Model's temperature: ${temperature.toFixed(
65
64
  2
66
65
  )}. Temperature controls the degree of randomness in generated output. A lower temperature setting will result in more predictable and less creative outputs while a higher temperature setting will result in more unpredictable and more creative outputs.`}
@@ -1,7 +1,7 @@
1
1
  import {useSize} from "@fibery/react/src/use-size";
2
2
  import React, {ComponentProps, forwardRef, useEffect, useRef, useState} from "react";
3
3
  import {duration, transition} from "./design-system";
4
- import _ from "lodash";
4
+ import noop from "lodash/noop";
5
5
  import {useComposedRefs} from "@fibery/react/src/use-composed-refs";
6
6
 
7
7
  type AnimatedHeightContainerProps = ComponentProps<"div"> & {
@@ -12,7 +12,7 @@ type AnimatedHeightContainerProps = ComponentProps<"div"> & {
12
12
 
13
13
  export const AnimatedHeightContainer = forwardRef<HTMLDivElement, AnimatedHeightContainerProps>(
14
14
  function AnimatedHeightContainerComponent(
15
- {className, noAnimation = false, onTransitionStart = _.noop, onTransitionEnd = _.noop, children, ...otherProps},
15
+ {className, noAnimation = false, onTransitionStart = noop, onTransitionEnd = noop, children, ...otherProps},
16
16
  ref
17
17
  ) {
18
18
  const measuredContainer = useRef<HTMLDivElement | null>(null);
@@ -46,7 +46,7 @@ export const AnimatedHeightContainer = forwardRef<HTMLDivElement, AnimatedHeight
46
46
  // Disable scroll so that overflowed contend won't show on focus
47
47
  // https://stackoverflow.com/questions/67694101/overflowed-content-is-showing-on-focus
48
48
  useEffect(() => {
49
- let cleanup = _.noop;
49
+ let cleanup = noop;
50
50
 
51
51
  if (animating && containerRef.current) {
52
52
  const element = containerRef.current;
@@ -1,4 +1,7 @@
1
1
  import {StyledInput, StyledTextArea} from "./input";
2
+ import {InputProps} from "antd/lib/input";
2
3
 
3
4
  export const AntInput = StyledInput;
4
5
  export const AntTextArea = StyledTextArea;
6
+
7
+ export type AntInputProps = InputProps;
@@ -4,7 +4,6 @@ import "antd/lib/input/style/css";
4
4
  import {css} from "@linaria/core";
5
5
  import {mobileRootSelector} from "../mobile-styles";
6
6
  import {opacity, textStyles, themeVars} from "../design-system";
7
-
8
7
  import {inputStyles} from "./styles";
9
8
  import {wrapWith} from "./utils";
10
9
 
@@ -3,16 +3,16 @@ import {border, layout, shadows, space, textStyles, themeVars, transition} from
3
3
 
4
4
  const inputVariables = {
5
5
  default: {
6
- "--input-border": themeVars.inputBorderColor,
7
- "--input-hover-border": `0 0 0 1px ${themeVars.opacity.opacity40}`,
8
- "--input-focus-border": `0 0 0 1px ${themeVars.opacity.opacity25}`,
9
- "--input-focus-shadow": `0 0 0 3px ${themeVars.focus}`,
6
+ "--input-border": `${themeVars.inputBorderColor} inset`,
7
+ "--input-hover-border": `0 0 0 1px ${themeVars.opacity.opacity40} inset`,
8
+ "--input-focus-border": `0 0 0 1px ${themeVars.opacity.opacity50} inset`,
9
+ "--input-focus-shadow": `0 0 0 2px ${themeVars.focus}`,
10
10
  },
11
11
  error: {
12
- "--input-border": `0 0 0 1px ${themeVars.warning}`,
13
- "--input-hover-border": `0 0 0 1px ${themeVars.warning}`,
14
- "--input-focus-border": `0 0 0 1px ${themeVars.warning}`,
15
- "--input-focus-shadow": `0 0 0 1px ${themeVars.warning}`,
12
+ "--input-border": `0 0 0 1px ${themeVars.warning} inset`,
13
+ "--input-hover-border": `0 0 0 1px ${themeVars.warning} inset`,
14
+ "--input-focus-border": `0 0 0 1px ${themeVars.warning} inset`,
15
+ "--input-focus-shadow": `0 0 0 2px ${themeVars.warning}`,
16
16
  },
17
17
  };
18
18
 
@@ -43,7 +43,7 @@ export const inputOverrides = {
43
43
  transition: `box-shadow ${transition}`,
44
44
  },
45
45
  focus: {
46
- boxShadow: `var(--input-focus-border, 0 0 0 1px ${themeVars.opacity.opacity25}) !important`,
46
+ boxShadow: `var(--input-focus-border, 0 0 0 1px ${themeVars.opacity.opacity25}), var(--input-focus-shadow) !important`,
47
47
  transition: `box-shadow ${transition}`,
48
48
  },
49
49
  disabled: {
@@ -1,9 +1,9 @@
1
1
  import {css} from "@linaria/core";
2
- import _ from "lodash";
3
- import {FC, Suspense, lazy} from "react";
2
+ import fromPairs from "lodash/fromPairs";
3
+ import {FC, lazy, Suspense} from "react";
4
4
  import {a11yColor} from "./a11y-color";
5
5
  import {AppIconWrapper} from "./app-icon-wrapper";
6
- import {getOpacities, getThemeValue, iconSize, layout} from "./design-system";
6
+ import {getOpacities, iconSize, layout} from "./design-system";
7
7
  import {Icon} from "./icons/Icon";
8
8
  import {useTheme} from "./theme-provider";
9
9
  import {useIsPhone} from "./use-is-phone";
@@ -89,7 +89,7 @@ type UglyExternalIconProps = Omit<UglyExternalIconImplProps, "appIconsLookup">;
89
89
 
90
90
  const UglyExternalIcon = lazy(async () => {
91
91
  const appIcons = await import("./appIcons.json");
92
- const appIconsLookup = _.fromPairs(
92
+ const appIconsLookup = fromPairs(
93
93
  appIcons.default.flatMap((appIcon) => appIcon.short_names.map((shortName) => [shortName, appIcon]))
94
94
  );
95
95
 
@@ -128,7 +128,7 @@ export const AppIcon: FC<AppIconProps> = ({icon, color, isSelected, ...rest}) =>
128
128
  const isPhone = useIsPhone();
129
129
  const defaultIconSize = isPhone ? layout.mobileSidebarIconSize : layout.newMenuIconSize;
130
130
  const {iconSize = defaultIconSize, containerSize = defaultIconSize} = rest;
131
- const iconColor = getThemeValue(theme.mode, {
131
+ const iconColor = theme.select({
132
132
  dark: a11yColor(getOpacities(color).opacity95, theme.menuBg, 5),
133
133
  light: a11yColor(getOpacities(color).opacity95, theme.whiteColor, 3),
134
134
  light2: a11yColor(getOpacities(color).opacity95, theme.menuBg, 5),
@@ -1,7 +1,7 @@
1
1
  import {css} from "@linaria/core";
2
2
  import cn from "classnames";
3
3
  import {ReactNode} from "react";
4
- import {border, getThemeValue, layout} from "./design-system";
4
+ import {border, layout} from "./design-system";
5
5
  import {useTheme} from "./theme-provider";
6
6
  import {useIsPhone} from "./use-is-phone";
7
7
 
@@ -34,8 +34,7 @@ export const AppIconWrapper = ({
34
34
  style={{backgroundColor: borderless ? "transparent" : color, width: containerSize, height: containerSize}}
35
35
  className={cn(
36
36
  appIconContainerStyle,
37
- !borderless &&
38
- getThemeValue(theme.mode, {light2: appIconGradientStyle, dark: appIconGradientStyle, light: null})
37
+ !borderless && theme.select({light2: appIconGradientStyle, dark: appIconGradientStyle, light: null})
39
38
  )}
40
39
  >
41
40
  {children}
package/src/app-icon.tsx CHANGED
@@ -3,7 +3,7 @@ import cx from "classnames";
3
3
  import {FC} from "react";
4
4
  import {a11yColor} from "./a11y-color";
5
5
  import {AppIconWithFallback} from "./app-icon-with-fallback";
6
- import {border, getOpacities, getThemeValue} from "./design-system";
6
+ import {border, getOpacities} from "./design-system";
7
7
  import {useTheme} from "./theme-provider";
8
8
 
9
9
  interface AppIconProps {
@@ -16,7 +16,7 @@ interface AppIconProps {
16
16
 
17
17
  export const AppIcon: FC<AppIconProps> = ({color, icon, containerSize = 54, iconSize = 36, className}) => {
18
18
  const theme = useTheme();
19
- const iconColor = getThemeValue(theme.mode, {
19
+ const iconColor = theme.select({
20
20
  dark: a11yColor(getOpacities(color).opacity95, theme.menuBg, 5),
21
21
  light: a11yColor(getOpacities(color).opacity95, theme.whiteColor, 3),
22
22
  light2: a11yColor(getOpacities(color).opacity95, theme.menuBg, 5),
@@ -24,7 +24,7 @@ export const AppIcon: FC<AppIconProps> = ({color, icon, containerSize = 54, icon
24
24
 
25
25
  const bgColor = theme.fns.getAppIconBackground(color);
26
26
 
27
- const appIconGradientStyle = getThemeValue(theme.mode, {
27
+ const appIconGradientStyle = theme.select({
28
28
  dark: "linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0))",
29
29
  light: "none",
30
30
  light2: "linear-gradient(180deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0))",
package/src/avatar.tsx CHANGED
@@ -1,5 +1,5 @@
1
1
  import {css} from "@linaria/core";
2
- import _ from "lodash";
2
+ import memoize from "lodash/memoize";
3
3
  import {CSSProperties, FC, memo, useCallback, useMemo, useState} from "react";
4
4
  import {getDarkenColor, getObjectColorMemoized, textStyles, themeVars} from "./design-system";
5
5
  import {FibermojiPlaceholder} from "./fibermoji-placeholder";
@@ -7,7 +7,7 @@ import {getShiftStyle} from "./icons/get-shift-style";
7
7
  import {useFeatures} from "./features";
8
8
  import {abbreviateName} from "./abbreviate-name";
9
9
 
10
- export const getContainerStyle = _.memoize((containerSize) => {
10
+ export const getContainerStyle = memoize((containerSize) => {
11
11
  return {
12
12
  width: containerSize,
13
13
  height: containerSize,
@@ -22,7 +22,7 @@ const statusIconPositionByIndex = [
22
22
  {top: 0, left: 0},
23
23
  ];
24
24
 
25
- const getStatusIconStyle = _.memoize(
25
+ const getStatusIconStyle = memoize(
26
26
  (size, index): CSSProperties => {
27
27
  const iconSize = Math.ceil(size / 2);
28
28
  const position = statusIconPositionByIndex[index];
@@ -139,7 +139,6 @@ export const BaseButton = forwardRef<HTMLButtonElement, BaseButtonProps>(
139
139
  data-variant={variant}
140
140
  className={cx(reset, baseButton, className)}
141
141
  ref={ref}
142
- // eslint-disable-next-line react/button-has-type
143
142
  type={type}
144
143
  {...buttonProps}
145
144
  >
@@ -3,7 +3,7 @@
3
3
  // so it's easier to find buttons with custom colors
4
4
 
5
5
  import {CSSProperties} from "@linaria/core";
6
- import {getDarkenColor, getOpacities, getThemeValue, ThemeColors, themeVars} from "../design-system";
6
+ import {getDarkenColor, getOpacities, ThemeColors, themeVars} from "../design-system";
7
7
  import {ButtonVariant} from "./base-button";
8
8
 
9
9
  export const makeButtonColors = (color: string, variant: ButtonVariant, theme: ThemeColors): CSSProperties => {
@@ -40,7 +40,7 @@ export const makeButtonColors = (color: string, variant: ButtonVariant, theme: T
40
40
 
41
41
  return {
42
42
  "--fibery-button-color": bgColor,
43
- "--fibery-button-hover-color": getThemeValue(theme.mode, {
43
+ "--fibery-button-hover-color": theme.select({
44
44
  light: getOpacities(color).opacity30,
45
45
  dark: getOpacities(color).opacity45,
46
46
  }),
package/src/checkbox.tsx CHANGED
@@ -127,7 +127,7 @@ const checkmarkStyle = css`
127
127
  opacity: 0;
128
128
  transition: transform 0.2s, opacity 0.2s;
129
129
  pointer-events: none;
130
- color: ${themeVars.inversedTextColor};
130
+ color: ${themeVars.whiteColor};
131
131
  width: ${layout.checkboxSize}px;
132
132
  height: ${layout.checkboxSize}px;
133
133
 
@@ -1,7 +1,7 @@
1
1
  import {css, cx} from "@linaria/core";
2
2
  import ArrowBottom from "./icons/react/ArrowBottom";
3
3
  import ArrowTop from "./icons/react/ArrowTop";
4
- import {ReactNode, useCallback} from "react";
4
+ import {ReactNode, useCallback, useEffect, useRef} from "react";
5
5
  import {useControllableState} from "@fibery/react/src/use-controllable-state";
6
6
  import {space} from "./design-system";
7
7
 
@@ -48,6 +48,16 @@ export const CollapsibleSection = ({
48
48
 
49
49
  const onToggle = useCallback(() => setIsCollapsed(!isCollapsed), [isCollapsed, setIsCollapsed]);
50
50
 
51
+ // Ugly one-time auto-expand logic, in case of collapsedDefault is changed from false to true shortly after render
52
+ // (i.e. when something is loaded asynchronously)
53
+ const expandedOnce = useRef(false);
54
+ useEffect(() => {
55
+ if (isCollapsed && !collapsedDefault && !expandedOnce.current) {
56
+ expandedOnce.current = true;
57
+ setIsCollapsed(false);
58
+ }
59
+ }, [isCollapsed, collapsedDefault, setIsCollapsed]);
60
+
51
61
  const collapsibleHeader = (
52
62
  <div
53
63
  tabIndex={0}
@@ -1,4 +1,3 @@
1
- /* eslint-disable max-lines */
2
1
  import {
3
2
  fromHSL,
4
3
  fromOKLCH,
@@ -8,32 +7,31 @@ import {
8
7
  getHsl,
9
8
  getLuminance,
10
9
  isValidColor,
11
- OKLCH,
12
- produceColor,
13
10
  toCss,
11
+ toOKLCH,
14
12
  } from "./color-utils";
15
13
 
16
14
  describe("color-utils", () => {
17
15
  describe("css", () => {
18
16
  it("normalizes hex to rgb", () => {
19
- expect(toCss("#ff0000")).toBe("rgb(255,0,0)");
17
+ expect(toCss("#ff0000")).toMatchInlineSnapshot(`"rgb(255,0,0)"`);
20
18
  });
21
19
 
22
20
  it("normalizes named colors to rgb", () => {
23
- expect(toCss("red")).toBe("rgb(255,0,0)");
24
- expect(toCss("blue")).toBe("rgb(0,0,255)");
21
+ expect(toCss("red")).toMatchInlineSnapshot(`"rgb(255,0,0)"`);
22
+ expect(toCss("blue")).toMatchInlineSnapshot(`"rgb(0,0,255)"`);
25
23
  });
26
24
 
27
25
  it("preserves alpha in rgba", () => {
28
- expect(toCss("rgba(255, 0, 0, 0.5)")).toBe("rgba(255,0,0,0.5)");
26
+ expect(toCss("rgba(255, 0, 0, 0.5)")).toMatchInlineSnapshot(`"rgba(255,0,0,0.5)"`);
29
27
  });
30
28
 
31
29
  it("converts hsl to rgb", () => {
32
- expect(toCss("hsl(0, 100%, 50%)")).toBe("rgb(255,0,0)");
30
+ expect(toCss("hsl(0, 100%, 50%)")).toMatchInlineSnapshot(`"rgb(255,0,0)"`);
33
31
  });
34
32
 
35
33
  it("returns fallback for invalid color", () => {
36
- expect(toCss("invalid")).toBe("rgb(73,120,212)");
34
+ expect(toCss("invalid")).toMatchInlineSnapshot(`"rgb(73,120,212)"`);
37
35
  });
38
36
  });
39
37
 
@@ -99,12 +97,12 @@ describe("color-utils", () => {
99
97
  describe("toCss", () => {
100
98
  it("converts to rgb for opaque colors", () => {
101
99
  const result = toCss("#ff0000");
102
- expect(result).toMatch(/rgb\(255,\s*0,\s*0\)/);
100
+ expect(result).toMatchInlineSnapshot(`"rgb(255,0,0)"`);
103
101
  });
104
102
 
105
103
  it("converts to rgba for colors with alpha", () => {
106
104
  const result = toCss("rgba(255, 0, 0, 0.5)");
107
- expect(result).toMatch(/rgba\(255,\s*0,\s*0,\s*0\.5\)/);
105
+ expect(result).toMatchInlineSnapshot(`"rgba(255,0,0,0.5)"`);
108
106
  });
109
107
  });
110
108
 
@@ -295,23 +293,15 @@ describe("color-utils", () => {
295
293
 
296
294
  describe("produceColor", () => {
297
295
  it("outputs oklch format when OKLCH space is specified", () => {
298
- expect(produceColor("#ff0000", [], OKLCH)).toMatchInlineSnapshot(`"oklch(62.796% 0.25768 29.234)"`);
299
- expect(produceColor("rgb(0, 128, 255)", [], OKLCH)).toMatchInlineSnapshot(`"oklch(61.517% 0.21082 256.1)"`);
300
- expect(produceColor("oklch(61.517% 0.21082 256.1)", [], OKLCH)).toMatchInlineSnapshot(
301
- `"oklch(61.517% 0.21082 256.1)"`
302
- );
296
+ expect(toOKLCH("#ff0000")).toMatchInlineSnapshot(`"oklch(62.796% 0.25768 29.234)"`);
297
+ expect(toOKLCH("rgb(0, 128, 255)")).toMatchInlineSnapshot(`"oklch(61.517% 0.21082 256.1)"`);
298
+ expect(toOKLCH("oklch(61.517% 0.21082 256.1)")).toMatchInlineSnapshot(`"oklch(61.517% 0.21082 256.1)"`);
303
299
  });
304
300
 
305
301
  it("parses display-p3 colors correctly (P3 space removed for bundle size)", () => {
306
- expect(produceColor("color(display-p3 0.992 0.992 0.978)", [], OKLCH)).toMatchInlineSnapshot(
307
- `"oklch(99.285% 0.00516 106.49)"`
308
- );
309
- expect(produceColor("color(display-p3 0.995 0.99 0.922)", [], OKLCH)).toMatchInlineSnapshot(
310
- `"oklch(98.841% 0.02505 102.92)"`
311
- );
312
- expect(produceColor("color(display-p3 0.997 0.982 0.749)", [], OKLCH)).toMatchInlineSnapshot(
313
- `"oklch(97.402% 0.08449 104.36)"`
314
- );
302
+ expect(toOKLCH("color(display-p3 0.992 0.992 0.978)")).toMatchInlineSnapshot(`"oklch(99.285% 0.00516 106.49)"`);
303
+ expect(toOKLCH("color(display-p3 0.995 0.99 0.922)")).toMatchInlineSnapshot(`"oklch(98.841% 0.02505 102.92)"`);
304
+ expect(toOKLCH("color(display-p3 0.997 0.982 0.749)")).toMatchInlineSnapshot(`"oklch(97.402% 0.08449 104.36)"`);
315
305
  });
316
306
  });
317
307
  });