@noya-app/noya-designsystem 0.1.94 → 0.1.96

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 (43) hide show
  1. package/.turbo/turbo-build.log +9 -9
  2. package/.turbo/turbo-typecheck.log +1 -0
  3. package/CHANGELOG.md +16 -0
  4. package/dist/index.css +1 -1
  5. package/dist/index.d.mts +158 -93
  6. package/dist/index.d.ts +158 -93
  7. package/dist/index.js +1307 -1034
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.mjs +1231 -962
  10. package/dist/index.mjs.map +1 -1
  11. package/package.json +6 -5
  12. package/src/__tests__/SharedDragProvider.test.ts +72 -29
  13. package/src/__tests__/fuzzyScorer.test.ts +49 -1
  14. package/src/components/Button.tsx +1 -1
  15. package/src/components/Combobox.tsx +23 -3
  16. package/src/components/ComboboxMenu.tsx +32 -15
  17. package/src/components/ContextMenu.tsx +26 -7
  18. package/src/components/DimensionInput.tsx +12 -2
  19. package/src/components/DropdownMenu.tsx +23 -8
  20. package/src/components/InputField.tsx +1 -0
  21. package/src/components/InspectorContainer.tsx +16 -1
  22. package/src/components/List.tsx +25 -3
  23. package/src/components/Popover.tsx +13 -1
  24. package/src/components/Section.tsx +35 -11
  25. package/src/components/SegmentedControl.tsx +14 -4
  26. package/src/components/Toolbar.tsx +16 -1
  27. package/src/components/ToolbarItems.tsx +322 -0
  28. package/src/components/Tooltip.tsx +8 -1
  29. package/src/components/__tests__/Card.test.tsx +2 -1
  30. package/src/components/__tests__/InputField.test.tsx +3 -1
  31. package/src/components/__tests__/ToolbarItems.test.tsx +205 -0
  32. package/src/components/internal/Menu.tsx +4 -1
  33. package/src/components/internal/TextInput.tsx +43 -2
  34. package/src/components/internal/__tests__/TextInput.test.tsx +79 -2
  35. package/src/components/listView/ListViewEditableRowTitle.tsx +37 -1
  36. package/src/components/listView/ListViewRow.tsx +3 -0
  37. package/src/components/listView/__tests__/ListViewEditableRowTitle.test.tsx +69 -0
  38. package/src/components/sorting/Sortable.tsx +4 -1
  39. package/src/index.tsx +0 -1
  40. package/src/utils/combobox.ts +22 -5
  41. package/src/utils/fuzzyScorer.ts +71 -0
  42. package/tailwind.config.ts +7 -0
  43. package/src/components/InspectorPrimitives.tsx +0 -188
@@ -31,6 +31,77 @@ export function fuzzyFilter({
31
31
  .sort((a, b) => b.score - a.score);
32
32
  }
33
33
 
34
+ /**
35
+ * Word-boundary filter for name-like lists (e.g. font families): ranks
36
+ * exact > prefix > word-start matches and only falls back to substring
37
+ * matches when nothing matches at a word boundary. Prevents queries like
38
+ * "Play" from surfacing every "* Display" family via mid-word substrings.
39
+ */
40
+ export function wordPrefixFilter({
41
+ items,
42
+ query,
43
+ }: {
44
+ items: string[];
45
+ query: string;
46
+ }): IScoredItem[] {
47
+ const normalizedQuery = query.trim().toLowerCase();
48
+
49
+ if (!normalizedQuery) {
50
+ return items.map((_, index): IScoredItem => ({ index, score: 0 }));
51
+ }
52
+
53
+ const boundaryMatches: IScoredItem[] = [];
54
+ const substringMatches: IScoredItem[] = [];
55
+
56
+ items.forEach((text, index) => {
57
+ const lower = text.toLowerCase();
58
+
59
+ const makeItem = (score: number, start: number): IScoredItem => ({
60
+ index,
61
+ score,
62
+ labelMatch: [{ start, end: start + normalizedQuery.length }],
63
+ });
64
+
65
+ if (lower === normalizedQuery) {
66
+ boundaryMatches.push({
67
+ index,
68
+ score: 4000,
69
+ labelMatch: [{ start: 0, end: text.length }],
70
+ });
71
+ return;
72
+ }
73
+
74
+ if (lower.startsWith(normalizedQuery)) {
75
+ boundaryMatches.push(makeItem(3000, 0));
76
+ return;
77
+ }
78
+
79
+ // Match at the start of any subsequent word (space/hyphen separated)
80
+ const wordStart = lower.search(
81
+ new RegExp(`[\\s-]${escapeRegExp(normalizedQuery)}`)
82
+ );
83
+ if (wordStart !== -1) {
84
+ boundaryMatches.push(makeItem(2000 - wordStart, wordStart + 1));
85
+ return;
86
+ }
87
+
88
+ const substringIndex = lower.indexOf(normalizedQuery);
89
+ if (substringIndex !== -1) {
90
+ substringMatches.push(makeItem(1, substringIndex));
91
+ }
92
+ });
93
+
94
+ // Only fall back to mid-word substring matches when nothing matched at a
95
+ // word boundary.
96
+ const result = boundaryMatches.length > 0 ? boundaryMatches : substringMatches;
97
+
98
+ return result.sort((a, b) => b.score - a.score);
99
+ }
100
+
101
+ function escapeRegExp(value: string): string {
102
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
103
+ }
104
+
34
105
  export type IToken =
35
106
  | { type: "text"; text: string }
36
107
  | { type: "match"; text: string };
@@ -5,6 +5,13 @@ import type { Config } from "tailwindcss";
5
5
 
6
6
  const config = {
7
7
  ...noyaConfig,
8
+ // Tailwind class names are confined to the packages that generate CSS: this
9
+ // package and @noya-app/inspector. The inspector package ships no Tailwind
10
+ // build of its own — its classes are generated here and reach apps through
11
+ // the dist/index.css that @noya-app/inspector re-exports/imports — so its
12
+ // sources are part of this build's content. Consuming packages/apps compose
13
+ // styled components instead of writing raw class names for them.
14
+ content: ["./src/**/*.{ts,tsx}", "../inspector/src/**/*.{ts,tsx}"],
8
15
  prefix: "n-",
9
16
  plugins: [containerQueries, typography],
10
17
  safelist: [
@@ -1,188 +0,0 @@
1
- import React, {
2
- ForwardedRef,
3
- forwardRef,
4
- LabelHTMLAttributes,
5
- memo,
6
- ReactNode,
7
- } from "react";
8
- import { cx } from "../utils/classNames";
9
- import { Spacer } from "./Spacer";
10
- import { textStyles } from "./Text";
11
-
12
- export const Section = forwardRef<
13
- HTMLDivElement,
14
- React.HTMLAttributes<HTMLDivElement>
15
- >(({ className, ...props }, ref) => (
16
- <div
17
- ref={ref}
18
- className={cx(`n-flex-none n-flex n-flex-col`, className)}
19
- {...props}
20
- />
21
- ));
22
-
23
- export const SectionHeader = forwardRef<
24
- HTMLDivElement,
25
- React.HTMLAttributes<HTMLDivElement>
26
- >(({ className, ...props }, ref) => (
27
- <div ref={ref} className={cx(`n-flex n-items-center`, className)} {...props} />
28
- ));
29
-
30
- type TitleTextStyle = "small" | "heading5" | "heading4" | "heading3";
31
-
32
- export const Title = forwardRef<
33
- HTMLDivElement,
34
- React.HTMLAttributes<HTMLDivElement> & {
35
- $textStyle?: TitleTextStyle;
36
- }
37
- >(({ className, $textStyle, ...props }, ref) => (
38
- <div
39
- ref={ref}
40
- className={cx(
41
- `n-flex n-select-none ${
42
- $textStyle
43
- ? `${textStyles[$textStyle]} n-text-text`
44
- : "n-font-sans n-text-label n-text-text-muted n-font-semibold"
45
- } `,
46
- className
47
- )}
48
- {...props}
49
- />
50
- ));
51
-
52
- export const Row = forwardRef<
53
- HTMLDivElement,
54
- React.HTMLAttributes<HTMLDivElement>
55
- >(({ className, ...props }, ref) => (
56
- <div
57
- ref={ref}
58
- className={cx(`n-flex-1 n-flex n-flex-row n-items-center`, className)}
59
- {...props}
60
- />
61
- ));
62
-
63
- export const Column = forwardRef<
64
- HTMLDivElement,
65
- React.HTMLAttributes<HTMLDivElement>
66
- >(({ className, ...props }, ref) => (
67
- <div
68
- ref={ref}
69
- className={cx(`n-flex-1 n-flex n-flex-col n-gap-1`, className)}
70
- {...props}
71
- />
72
- ));
73
-
74
- export const Checkbox = forwardRef<
75
- HTMLInputElement,
76
- React.InputHTMLAttributes<HTMLInputElement>
77
- >(({ className, ...props }, ref) => (
78
- <input
79
- ref={ref}
80
- type="checkbox"
81
- className={cx(`n-m-0 focus:n-z-interactable`, className)}
82
- {...props}
83
- />
84
- ));
85
-
86
- export const Text = forwardRef<
87
- HTMLSpanElement,
88
- React.HTMLAttributes<HTMLSpanElement>
89
- >(({ className, ...props }, ref) => (
90
- <span
91
- ref={ref}
92
- className={cx(`n-font-sans n-text-heading5 n-font-normal`, className)}
93
- {...props}
94
- />
95
- ));
96
-
97
- export const VerticalSeparator = () => (
98
- <Spacer.Vertical size="inspector-v-separator" />
99
- );
100
-
101
- export const HorizontalSeparator = () => (
102
- <Spacer.Horizontal size="inspector-h-separator" />
103
- );
104
-
105
- // const SliderRowLabel = forwardRef<
106
- // HTMLSpanElement,
107
- // React.HTMLAttributes<HTMLSpanElement>
108
- // >(({ className, ...props }, ref) => (
109
- // <span
110
- // ref={ref}
111
- // className={`font-sans text-heading5 font-normal text-text-muted -mb-[6px] `, className}
112
- // {...props}
113
- // />
114
- // ));
115
-
116
- export const RowLabel = forwardRef(function RowLabel(
117
- {
118
- className,
119
- $textStyle,
120
- ...props
121
- }: LabelHTMLAttributes<HTMLLabelElement> & {
122
- $textStyle?: TitleTextStyle | "label";
123
- },
124
- ref: ForwardedRef<HTMLLabelElement>
125
- ) {
126
- return (
127
- <label
128
- ref={ref}
129
- className={cx(
130
- `n-font-sans n-text-label n-uppercase n-flex n-items-center n-leading-[19px] n-font-bold n-text-text-subtle n-z-label ${
131
- $textStyle && textStyles[$textStyle]
132
- } `,
133
- className
134
- )}
135
- {...props}
136
- />
137
- );
138
- });
139
-
140
- interface LabeledRowProps {
141
- id?: string;
142
- children: ReactNode;
143
- label: ReactNode;
144
- labelTextStyle?: TitleTextStyle;
145
- right?: ReactNode;
146
- className?: string;
147
- }
148
-
149
- export const LabeledRow = memo(function LabeledRow({
150
- id,
151
- children,
152
- label,
153
- labelTextStyle,
154
- right,
155
- className,
156
- }: LabeledRowProps) {
157
- return (
158
- <Row id={id} className={className}>
159
- <Column>
160
- <RowLabel $textStyle={labelTextStyle}>
161
- {label}
162
- {right && <Spacer.Horizontal />}
163
- {right}
164
- </RowLabel>
165
- <Row>{children}</Row>
166
- </Column>
167
- </Row>
168
- );
169
- });
170
-
171
- // interface LabeledSliderRowProps {
172
- // children: ReactNode;
173
- // label: string;
174
- // }
175
-
176
- // export const LabeledSliderRow = memo(function LabeledSliderRow({
177
- // children,
178
- // label,
179
- // }: LabeledSliderRowProps) {
180
- // return (
181
- // <Row>
182
- // <Column>
183
- // <SliderRowLabel>{label}</SliderRowLabel>
184
- // <Row>{children}</Row>
185
- // </Column>
186
- // </Row>
187
- // );
188
- // });