@fibery/ui-kit 4.0.0 → 4.1.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 (54) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/esfint.config.mjs +0 -17
  3. package/package.json +5 -6
  4. package/scripts/generate-icons.mjs +45 -44
  5. package/src/actions-menu/actions-menu-item.tsx +1 -1
  6. package/src/actions-menu/context-actions-menu.tsx +1 -1
  7. package/src/animated-height-container.tsx +1 -1
  8. package/src/antd/ant-modal.tsx +21 -21
  9. package/src/antd/styles.ts +2 -0
  10. package/src/app-icon-with-fallback.tsx +3 -7
  11. package/src/app-icon-wrapper.tsx +1 -13
  12. package/src/breadcrumb.tsx +5 -1
  13. package/src/color-utils.test.ts +2 -2
  14. package/src/color-utils.ts +2 -2
  15. package/src/copy-to-clipboard.ts +14 -3
  16. package/src/date-picker/contexts.ts +6 -3
  17. package/src/date-picker/date-range-picker.tsx +1 -2
  18. package/src/date-picker/single-date-picker.tsx +1 -2
  19. package/src/day-select/iso-week-day-select.tsx +2 -2
  20. package/src/design-system/colors.ts +3 -3
  21. package/src/emoji-picker/icon-emoji-picker.tsx +3 -3
  22. package/src/icons/ast/index.tsx +446 -446
  23. package/src/icons/icon.tsx +14 -2
  24. package/src/icons/icons-integrity.test.ts +145 -0
  25. package/src/icons/react/index.tsx +446 -446
  26. package/src/images-gallery/images-gallery.tsx +2 -2
  27. package/src/images-gallery/slide-buttons.tsx +2 -4
  28. package/src/lists/actions-menu-row-surface.tsx +1 -1
  29. package/src/media-query-utils.ts +1 -1
  30. package/src/mobile-keyboard-aware-popup.tsx +1 -1
  31. package/src/number-input/decimal.ts +6 -9
  32. package/src/number-input/number-input-inline-with-autosize.tsx +1 -1
  33. package/src/online-users.tsx +4 -5
  34. package/src/palettes/inspect.defs.colors.neutral-arch.test.ts +4 -4
  35. package/src/palettes/inspect.defs.colors.neutral-user.test.ts +4 -4
  36. package/src/palettes/inspect.defs.colors.warm-arch.test.ts +4 -4
  37. package/src/palettes/inspect.defs.colors.warm-user.test.ts +4 -4
  38. package/src/popover/index.tsx +4 -4
  39. package/src/popover/mobile-popover-context.tsx +1 -1
  40. package/src/popover/modifiers.tsx +1 -1
  41. package/src/rich-input-loader.tsx +1 -1
  42. package/src/select/components/menu-list-virtua.tsx +15 -16
  43. package/src/select/components/menu-list-virtualized.tsx +26 -29
  44. package/src/select/components/menu.tsx +3 -3
  45. package/src/select/index.tsx +1 -1
  46. package/src/select/reflection.ts +4 -5
  47. package/src/select/select.tsx +3 -3
  48. package/src/select/styles.ts +1 -7
  49. package/src/select/util.ts +2 -2
  50. package/src/theming.generated.css +8 -8
  51. package/src/theming.generated.ts +8 -8
  52. package/src/type-badge.tsx +1 -2
  53. package/src/use-on-screen-keyboard-data.tsx +1 -1
  54. package/src/with-data.tsx +1 -1
@@ -4,6 +4,11 @@ import {forwardRef} from "react";
4
4
  import {border, iconSize as defaultIconSize, themeVars, viewBoxSize} from "../design-system";
5
5
  import {useTheme} from "../theme-provider";
6
6
  import DatabaseSvg from "./ast/database";
7
+ import ObjChildSvg from "./ast/obj-child";
8
+ import ObjM2MSvg from "./ast/obj-m2m";
9
+ import ObjO2OSvg from "./ast/obj-o2o";
10
+ import ObjParentSvg from "./ast/obj-parent";
11
+ import ObjSelfSvg from "./ast/obj-self";
7
12
  import {generateIconFromAst} from "./generate-icon-from-ast";
8
13
  import type {IconProps} from "./types";
9
14
 
@@ -44,8 +49,15 @@ export const Icon = forwardRef<SVGSVGElement, IconProps>(function Icon(
44
49
  ) {
45
50
  const theme = useTheme();
46
51
  let resolvedColor: string | null | undefined;
47
- if (typeof color === "string" && !colorProcessed && !color.startsWith("var(")) {
48
- if (icon === DatabaseSvg) {
52
+ if (typeof color === "string" && !colorProcessed && !color.startsWith("var(") && color !== "currentColor") {
53
+ if (
54
+ icon === DatabaseSvg ||
55
+ icon === ObjSelfSvg ||
56
+ icon === ObjO2OSvg ||
57
+ icon === ObjChildSvg ||
58
+ icon === ObjParentSvg ||
59
+ icon === ObjM2MSvg
60
+ ) {
49
61
  resolvedColor = theme.fns.badge(color).bg;
50
62
  } else {
51
63
  resolvedColor = theme.fns.icon(color).fg;
@@ -0,0 +1,145 @@
1
+ /// <reference types="node-types-local" />
2
+ import {readdirSync, readFileSync} from "node:fs";
3
+ import path from "node:path";
4
+ import {fileURLToPath} from "node:url";
5
+
6
+ import {glob} from "glob";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+ const ICONS_DIR = path.resolve(__dirname);
11
+ const SVG_DIR = path.join(ICONS_DIR, "svg");
12
+ const AST_DIR = path.join(ICONS_DIR, "ast");
13
+ const REACT_DIR = path.join(ICONS_DIR, "react");
14
+ const FIX_CMD = "pnpm -filter ui-kit generate-icons";
15
+
16
+ const WORD_RE = /[A-Z]+(?![a-z])|[A-Z]?[a-z]+|\d+/g;
17
+ const words = (s: string) => String(s).match(WORD_RE) ?? [];
18
+ const cap = (w: string) => w[0].toUpperCase() + w.slice(1).toLowerCase();
19
+ const pascalCase = (s: string) => words(s).map(cap).join("");
20
+
21
+ function toFileBase(relativeSvgPath: string): string {
22
+ return relativeSvgPath.replaceAll("/", "-").replace(".svg", "");
23
+ }
24
+
25
+ function toIdentifier(relativeSvgPath: string): string {
26
+ return pascalCase(relativeSvgPath.replaceAll("/", "-").replace(".svg", ""));
27
+ }
28
+
29
+ function listFiles(dir: string, ext: string): string[] {
30
+ return readdirSync(dir)
31
+ .filter((f) => f.endsWith(ext) && f !== `index${ext}` && f !== "index.tsx" && f !== "index.ts")
32
+ .map((f) => f.slice(0, -ext.length));
33
+ }
34
+
35
+ function readBarrelExports(dir: string): Map<string, string> {
36
+ const indexFile = readdirSync(dir).find((f) => f === "index.tsx" || f === "index.ts");
37
+ if (!indexFile) {
38
+ return new Map();
39
+ }
40
+ const content = readFileSync(path.join(dir, indexFile), "utf-8");
41
+ const exports = new Map<string, string>();
42
+ for (const line of content.split("\n")) {
43
+ const match = line.match(/export\s*\{\s*default\s+as\s+(\w+)\s*\}\s*from\s*["']\.\/([^"']+)["']/);
44
+ if (match) {
45
+ exports.set(match[1], match[2]);
46
+ }
47
+ }
48
+ return exports;
49
+ }
50
+
51
+ const svgPaths: string[] = glob.sync("**/*.svg", {cwd: SVG_DIR});
52
+ const expected = svgPaths.map((p) => ({file: toFileBase(p), id: toIdentifier(p)}));
53
+
54
+ describe("ui-kit icon generation integrity", () => {
55
+ test("every SVG has a corresponding AST file", () => {
56
+ const astFiles = new Set(listFiles(AST_DIR, ".ts"));
57
+ const missing = expected.filter((e) => !astFiles.has(e.file)).map((e) => e.file);
58
+ if (missing.length > 0) {
59
+ throw new Error(`Missing AST files. Run '${FIX_CMD}' to fix.\n${missing.join("\n")}`);
60
+ }
61
+ });
62
+
63
+ test("every SVG has a corresponding React file", () => {
64
+ const reactFiles = new Set(listFiles(REACT_DIR, ".tsx"));
65
+ const missing = expected.filter((e) => !reactFiles.has(e.file)).map((e) => e.file);
66
+ if (missing.length > 0) {
67
+ throw new Error(`Missing React files. Run '${FIX_CMD}' to fix.\n${missing.join("\n")}`);
68
+ }
69
+ });
70
+
71
+ test("no orphan AST files without source SVG", () => {
72
+ const expectedSet = new Set(expected.map((e) => e.file));
73
+ const orphans = listFiles(AST_DIR, ".ts").filter((name) => !expectedSet.has(name));
74
+ if (orphans.length > 0) {
75
+ throw new Error(`Orphan AST files (no source SVG). Run '${FIX_CMD}' to fix.\n${orphans.join("\n")}`);
76
+ }
77
+ });
78
+
79
+ test("no orphan React files without source SVG", () => {
80
+ const expectedSet = new Set(expected.map((e) => e.file));
81
+ const orphans = listFiles(REACT_DIR, ".tsx").filter((name) => !expectedSet.has(name));
82
+ if (orphans.length > 0) {
83
+ throw new Error(`Orphan React files (no source SVG). Run '${FIX_CMD}' to fix.\n${orphans.join("\n")}`);
84
+ }
85
+ });
86
+
87
+ test("react/index.tsx barrel exports match SVG sources exactly", () => {
88
+ const exports = readBarrelExports(REACT_DIR);
89
+ const errors: string[] = [];
90
+ for (const {id, file} of expected) {
91
+ if (!exports.has(id)) {
92
+ errors.push(`Missing export: ${id} from ./${file}`);
93
+ } else if (exports.get(id) !== file) {
94
+ errors.push(`Wrong path for ${id}: got ./${exports.get(id)}, expected ./${file}`);
95
+ }
96
+ }
97
+ const expectedIds = new Set(expected.map((e) => e.id));
98
+ for (const id of exports.keys()) {
99
+ if (!expectedIds.has(id)) {
100
+ errors.push(`Extra export: ${id}`);
101
+ }
102
+ }
103
+ if (errors.length > 0) {
104
+ throw new Error(`react/index.tsx barrel mismatch. Run '${FIX_CMD}' to fix.\n${errors.join("\n")}`);
105
+ }
106
+ });
107
+
108
+ test("ast/index.tsx barrel exports match SVG sources exactly", () => {
109
+ const exports = readBarrelExports(AST_DIR);
110
+ const errors: string[] = [];
111
+ for (const {id, file} of expected) {
112
+ if (!exports.has(id)) {
113
+ errors.push(`Missing export: ${id} from ./${file}`);
114
+ } else if (exports.get(id) !== file) {
115
+ errors.push(`Wrong path for ${id}: got ./${exports.get(id)}, expected ./${file}`);
116
+ }
117
+ }
118
+ const expectedIds = new Set(expected.map((e) => e.id));
119
+ for (const id of exports.keys()) {
120
+ if (!expectedIds.has(id)) {
121
+ errors.push(`Extra export: ${id}`);
122
+ }
123
+ }
124
+ if (errors.length > 0) {
125
+ throw new Error(`ast/index.tsx barrel mismatch. Run '${FIX_CMD}' to fix.\n${errors.join("\n")}`);
126
+ }
127
+ });
128
+
129
+ test("each React component file imports its matching AST module", () => {
130
+ const errors: string[] = [];
131
+ for (const {id, file} of expected) {
132
+ const reactPath = path.join(REACT_DIR, `${file}.tsx`);
133
+ const content = readFileSync(reactPath, "utf-8");
134
+ if (!content.includes(`from "../ast/${file}"`)) {
135
+ errors.push(`${file}.tsx: missing import from ../ast/${file}`);
136
+ }
137
+ if (!content.includes(`export default ${id}`)) {
138
+ errors.push(`${file}.tsx: missing 'export default ${id}'`);
139
+ }
140
+ }
141
+ if (errors.length > 0) {
142
+ throw new Error(`React component drift. Run '${FIX_CMD}' to fix.\n${errors.join("\n")}`);
143
+ }
144
+ });
145
+ });