@noya-app/noya-designsystem 0.1.36 → 0.1.37

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noya-app/noya-designsystem",
3
- "version": "0.1.36",
3
+ "version": "0.1.37",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
@@ -0,0 +1,76 @@
1
+ import React, { forwardRef, InputHTMLAttributes, memo } from "react";
2
+
3
+ interface CheckboxProps
4
+ extends Omit<InputHTMLAttributes<HTMLInputElement>, "type"> {
5
+ colorScheme?: "primary" | "secondary";
6
+ }
7
+
8
+ const colorSchemeStyles = {
9
+ normal: "text-text-subtle",
10
+ primary: "text-primary",
11
+ secondary: "text-secondary",
12
+ };
13
+
14
+ export const Checkbox = memo(
15
+ forwardRef<HTMLInputElement, CheckboxProps>(function Checkbox(
16
+ { className, colorScheme = "secondary", ...props },
17
+ ref
18
+ ) {
19
+ return (
20
+ <div
21
+ className={`relative inline-flex w-[19px] h-[19px] ${colorSchemeStyles[colorScheme]} ${className ?? ""}`}
22
+ >
23
+ <input
24
+ ref={ref}
25
+ type="checkbox"
26
+ className={`
27
+ w-full
28
+ h-full
29
+ peer
30
+ appearance-none
31
+ rounded
32
+ bg-input-background
33
+ transition-colors
34
+ border
35
+ border-input-border
36
+ disabled:opacity-50
37
+ disabled:cursor-not-allowed
38
+ focus:ring-2
39
+ focus:ring-primary
40
+ focus:ring-offset-1
41
+ `}
42
+ style={{
43
+ width: "27px",
44
+ ...props.style,
45
+ }}
46
+ {...props}
47
+ />
48
+ <svg
49
+ className={`
50
+ absolute
51
+ inset-0
52
+ h-[27px]
53
+ w-[27px]
54
+ pointer-events-none
55
+ stroke-white
56
+ opacity-0
57
+ peer-checked:opacity-100
58
+ `}
59
+ style={{
60
+ strokeWidth: "1.3",
61
+ }}
62
+ viewBox="0 0 16 16"
63
+ fill="none"
64
+ xmlns="http://www.w3.org/2000/svg"
65
+ >
66
+ <path
67
+ d="M3 6L4.5 7.5L8 4"
68
+ strokeLinecap="round"
69
+ strokeLinejoin="round"
70
+ stroke="currentColor"
71
+ />
72
+ </svg>
73
+ </div>
74
+ );
75
+ })
76
+ );
@@ -1,31 +1,18 @@
1
- import React, { memo, ReactNode } from "react";
2
- import { Spacer } from "./Spacer";
3
- import { textStyles } from "./Text";
1
+ import React, { forwardRef, LabelHTMLAttributes, memo } from "react";
4
2
 
5
- /* ----------------------------------------------------------------------------
6
- * Root
7
- * ------------------------------------------------------------------------- */
3
+ interface LabelProps extends LabelHTMLAttributes<HTMLLabelElement> {}
8
4
 
9
- interface LabelRootProps {
10
- label: ReactNode;
11
- children: ReactNode;
12
- }
13
-
14
- function LabelRoot({ label, children }: LabelRootProps) {
15
- return (
16
- <span className={`flex-none relative outline-none border-0 min-w-0 text-left flex flex-col items-center`}>
17
- {children}
18
- {label && (
19
- <>
20
- <Spacer.Vertical size={2} />
21
- <span className={`${textStyles.small} text-text-muted text-xs flex-none min-w-0 tracking-[0.4px] whitespace-pre`}>{label}</span>
22
-
23
- </>
24
- )}
25
- </span>
26
- );
27
- }
28
-
29
- export namespace Label {
30
- export const Root = memo(LabelRoot);
31
- }
5
+ export const Label = memo(
6
+ forwardRef<HTMLLabelElement, LabelProps>(function Label(
7
+ { className, ...props },
8
+ ref
9
+ ) {
10
+ return (
11
+ <label
12
+ ref={ref}
13
+ className={`font-sans text-label uppercase flex items-center leading-[19px] select-none font-bold text-text-muted ${className ?? ""}`}
14
+ {...props}
15
+ />
16
+ );
17
+ })
18
+ );
@@ -1,11 +1,15 @@
1
- import { DropdownChevronIcon } from "@noya-app/noya-icons";
1
+ import {
2
+ ChevronDownIcon,
3
+ ChevronUpIcon,
4
+ DropdownChevronIcon,
5
+ } from "@noya-app/noya-icons";
2
6
  import * as Select from "@radix-ui/react-select";
3
7
  import { type SelectProps } from "@radix-ui/react-select";
4
8
  import React, { CSSProperties, memo, useMemo } from "react";
5
9
  import { Button } from "./Button";
6
10
  import { renderIcon } from "./Icons";
7
- import { Spacer } from "./Spacer";
8
11
  import { MenuItem, RegularMenuItem, styles } from "./internal/Menu";
12
+ import { Spacer } from "./Spacer";
9
13
 
10
14
  type Props<T extends string> = {
11
15
  id?: string;
@@ -25,11 +29,17 @@ const readOnlyStyle: CSSProperties = {
25
29
  textAlign: "left",
26
30
  };
27
31
 
28
- const textStyle: CSSProperties = {
29
- fontSize: "0.85rem",
30
- };
32
+ const labelStyles = `font-sans text-label uppercase text-text-disabled leading-[19px] font-bold text-[60%] pointer-events-none flex items-center mr-1.5`;
31
33
 
32
- const labelStyles = `font-sans text-label uppercase text-text-disabled leading-[19px] absolute inset-0 font-bold text-[60%] pointer-events-none flex items-center justify-end pr-6`;
34
+ const scrollButtonStyles = `
35
+ flex
36
+ items-center
37
+ justify-center
38
+ h-[25px]
39
+ cursor-default
40
+ text-text-muted
41
+ hover:text-text
42
+ `;
33
43
 
34
44
  export const SelectMenu = memo(function SelectMenu<T extends string = string>({
35
45
  id,
@@ -65,9 +75,7 @@ export const SelectMenu = memo(function SelectMenu<T extends string = string>({
65
75
  <Spacer.Horizontal inline size={6} />
66
76
  </>
67
77
  )}
68
- <span style={{ ...textStyle }} className="flex flex-1">
69
- {selectedItem?.title ?? value}
70
- </span>
78
+ <span className="flex flex-1">{selectedItem?.title ?? value}</span>
71
79
  </Button>
72
80
  ),
73
81
  [icon, id, style, className, disabled, value, selectedItem]
@@ -78,7 +86,7 @@ export const SelectMenu = memo(function SelectMenu<T extends string = string>({
78
86
  <Select.SelectTrigger asChild>
79
87
  <Button
80
88
  id={id}
81
- style={{ ...textStyle, ...style }}
89
+ style={style}
82
90
  className={`${className ?? "w-full"} flex-1`}
83
91
  disabled={disabled}
84
92
  >
@@ -88,25 +96,22 @@ export const SelectMenu = memo(function SelectMenu<T extends string = string>({
88
96
  <Spacer.Horizontal inline size={6} />
89
97
  </>
90
98
  )}
91
- <span className="flex flex-1">
99
+ <span className="flex flex-1 mr-1.5">
92
100
  <Select.Value placeholder={placeholder} />
93
101
  </span>
94
- <Spacer.Horizontal inline size={6} />
102
+ {label && <span className={labelStyles}>{label}</span>}
95
103
  <DropdownChevronIcon />
96
104
  </Button>
97
105
  </Select.SelectTrigger>
98
106
  ),
99
- [icon, id, style, className, disabled, placeholder]
107
+ [id, style, className, disabled, icon, placeholder, label]
100
108
  );
101
109
 
102
110
  if (readOnly) {
103
111
  return label ? (
104
112
  <div className="flex flex-col relative">
105
113
  {readOnlyButton}
106
- <label
107
- className={labelStyles}
108
- htmlFor={id}
109
- >
114
+ <label className={labelStyles} htmlFor={id}>
110
115
  {label}
111
116
  </label>
112
117
  </div>
@@ -117,20 +122,12 @@ export const SelectMenu = memo(function SelectMenu<T extends string = string>({
117
122
 
118
123
  return (
119
124
  <Select.Root value={value} onValueChange={onSelect} open={open}>
120
- {label ? (
121
- <div className="flex-1 flex-col relative">
122
- {trigger}
123
- {label && (
124
- <label className={labelStyles} htmlFor={id}>
125
- {label}
126
- </label>
127
- )}
128
- </div>
129
- ) : (
130
- trigger
131
- )}
125
+ {trigger}
132
126
  <Select.Portal>
133
127
  <Select.Content className={styles.contentStyle}>
128
+ <Select.ScrollUpButton className={scrollButtonStyles}>
129
+ <ChevronUpIcon />
130
+ </Select.ScrollUpButton>
134
131
  <Select.Viewport>
135
132
  {menuItems.map((menuItem) => {
136
133
  if (typeof menuItem === "string") {
@@ -146,6 +143,9 @@ export const SelectMenu = memo(function SelectMenu<T extends string = string>({
146
143
  );
147
144
  })}
148
145
  </Select.Viewport>
146
+ <Select.ScrollDownButton className={scrollButtonStyles}>
147
+ <ChevronDownIcon />
148
+ </Select.ScrollDownButton>
149
149
  </Select.Content>
150
150
  </Select.Portal>
151
151
  </Select.Root>
@@ -156,21 +156,27 @@ const SelectItem = React.forwardRef(
156
156
  (
157
157
  {
158
158
  children,
159
- icon, disabled,
159
+ icon,
160
+ disabled,
160
161
  ...props
161
162
  }: Select.SelectItemProps & { icon?: React.ReactNode },
162
163
  forwardedRef: React.ForwardedRef<HTMLDivElement>
163
164
  ) => {
164
165
  return (
165
- <Select.Item className={styles.itemStyle({disabled})} disabled={disabled} {...props} ref={forwardedRef}>
166
+ <Select.Item
167
+ className={styles.itemStyle({ disabled })}
168
+ disabled={disabled}
169
+ {...props}
170
+ ref={forwardedRef}
171
+ >
166
172
  {icon && (
167
173
  <>
168
174
  {renderIcon(icon)}
169
175
  <Spacer.Horizontal size={8} />
170
176
  </>
171
177
  )}
172
- <Select.ItemText style={textStyle}>{children}</Select.ItemText>
178
+ <Select.ItemText>{children}</Select.ItemText>
173
179
  </Select.Item>
174
180
  );
175
181
  }
176
- );
182
+ );
@@ -4,6 +4,7 @@ import React from "react";
4
4
  type SwitchColorScheme = "normal" | "primary" | "secondary";
5
5
 
6
6
  interface Props {
7
+ id?: string;
7
8
  value: boolean;
8
9
  onChange: (value: boolean) => void;
9
10
  /** @default normal */
@@ -12,6 +13,7 @@ interface Props {
12
13
  }
13
14
 
14
15
  export const Switch = function Switch({
16
+ id,
15
17
  value,
16
18
  onChange,
17
19
  colorScheme = "normal",
@@ -19,14 +21,15 @@ export const Switch = function Switch({
19
21
  }: Props) {
20
22
  return (
21
23
  <SwitchPrimitive.Root
24
+ id={id}
22
25
  checked={value}
23
26
  disabled={disabled}
24
27
  onCheckedChange={(newValue) => {
25
28
  onChange(newValue);
26
29
  }}
27
- className={`all-unset w-8 h-[19px] bg-active-background rounded-full relative cursor-pointer [webkit-tap-highlight-color:rgba(0,0,0,0)] focus:ring-2 focus:outline-primary focus:ring-offset-[1px] data-[state=checked]:bg-primary ${colorScheme === "secondary" && "data-[state=checked]:bg-secondary"}`}
30
+ className={`all-unset w-8 h-[19px] bg-active-background rounded-full relative cursor-pointer [webkit-tap-highlight-color:rgba(0,0,0,0)] focus:ring-2 focus:ring-primary focus:ring-offset-[1px] outline-none data-[state=checked]:bg-primary ${colorScheme === "secondary" && "data-[state=checked]:bg-secondary"}`}
28
31
  >
29
- <SwitchPrimitive.Thumb className="block w-[15px] h-[15px] bg-white rounded-full transition-transform translate-x-[2px] data-[state=checked]:translate-x-[15px]"/>
32
+ <SwitchPrimitive.Thumb className="block w-[15px] h-[15px] bg-white rounded-full transition-transform translate-x-[2px] data-[state=checked]:translate-x-[15px]" />
30
33
  </SwitchPrimitive.Root>
31
34
  );
32
35
  };
package/src/index.tsx CHANGED
@@ -17,6 +17,7 @@ declare module "react" {
17
17
  export * from "./components/ActivityIndicator";
18
18
  export * from "./components/Avatar";
19
19
  export * from "./components/Button";
20
+ export * from "./components/Checkbox";
20
21
  export * from "./components/Chip";
21
22
  export * from "./components/ContextMenu";
22
23
  export * from "./components/Dialog";
@@ -1,4 +1,26 @@
1
- import { Children, cloneElement, isValidElement, ReactNode } from "react";
1
+ import {
2
+ Children,
3
+ cloneElement,
4
+ Fragment,
5
+ isValidElement,
6
+ ReactNode,
7
+ } from "react";
8
+
9
+ interface SeparatorOptions {
10
+ traverseFragments?: boolean;
11
+ }
12
+
13
+ function flattenChildren(
14
+ children: ReactNode,
15
+ traverseFragments: boolean
16
+ ): ReactNode[] {
17
+ return Children.toArray(children).flatMap((child) => {
18
+ if (traverseFragments && isValidElement(child) && child.type === Fragment) {
19
+ return flattenChildren(child.props.children, traverseFragments);
20
+ }
21
+ return child;
22
+ });
23
+ }
2
24
 
3
25
  function createKey(key: string | number) {
4
26
  return `s-${key}`;
@@ -6,9 +28,13 @@ function createKey(key: string | number) {
6
28
 
7
29
  export default function withSeparatorElements(
8
30
  elements: ReactNode,
9
- separator: ReactNode | (() => ReactNode)
31
+ separator: ReactNode | (() => ReactNode),
32
+ options?: SeparatorOptions
10
33
  ) {
11
- const childrenArray = Children.toArray(elements);
34
+ const childrenArray = flattenChildren(
35
+ elements,
36
+ options?.traverseFragments ?? true
37
+ );
12
38
 
13
39
  for (let i = childrenArray.length - 1; i > 0; i--) {
14
40
  let sep =
@@ -216,8 +216,9 @@ const config: Config = {
216
216
  "underline",
217
217
  "-left-2.5",
218
218
  "-mr-2",
219
- "border-gray-200"
219
+ "border-gray-200",
220
+ "gap-1.5",
220
221
  ],
221
222
  };
222
223
 
223
- export default config
224
+ export default config;
package/tsconfig.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "extends": "@repo/typescript-config/library.json",
3
3
  "compilerOptions": {
4
- "types": ["tailwindcss/types"],
4
+ "types": ["tailwindcss/types", "bun-types"],
5
5
  },
6
6
  "exclude": ["node_modules", "dist"],
7
7
  }