@valbuild/ui 0.21.2 → 0.22.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 (70) hide show
  1. package/.storybook/theme.css +5 -1
  2. package/components.json +16 -0
  3. package/dist/valbuild-ui.cjs.d.ts +11 -7
  4. package/dist/valbuild-ui.cjs.js +43607 -33216
  5. package/dist/valbuild-ui.esm.js +48313 -37938
  6. package/fix-server-hack.js +45 -0
  7. package/fullscreen.vite.config.ts +11 -0
  8. package/index.html +13 -0
  9. package/package.json +52 -13
  10. package/server/dist/manifest.json +16 -0
  11. package/server/dist/style.css +2145 -0
  12. package/server/dist/valbuild-ui-main.cjs.js +74441 -0
  13. package/server/dist/valbuild-ui-main.esm.js +74442 -0
  14. package/server/dist/valbuild-ui-server.cjs.js +19 -2
  15. package/server/dist/valbuild-ui-server.esm.js +19 -2
  16. package/server.vite.config.ts +2 -0
  17. package/src/App.tsx +73 -0
  18. package/src/assets/icons/Logo.tsx +103 -0
  19. package/src/components/Button.tsx +10 -2
  20. package/src/components/Dropdown.tsx +2 -2
  21. package/src/components/{dashboard/Grid.stories.tsx → Grid.stories.tsx} +8 -17
  22. package/src/components/{dashboard/Grid.tsx → Grid.tsx} +36 -23
  23. package/src/components/RichTextEditor/ContentEditable.tsx +109 -1
  24. package/src/components/RichTextEditor/Plugins/Toolbar.tsx +2 -2
  25. package/src/components/RichTextEditor/RichTextEditor.tsx +1 -1
  26. package/src/components/ValFormField.tsx +576 -0
  27. package/src/components/ValFullscreen.tsx +1283 -0
  28. package/src/components/ValMenu.tsx +65 -13
  29. package/src/components/ValOverlay.tsx +32 -338
  30. package/src/components/ValWindow.tsx +12 -9
  31. package/src/components/dashboard/FormGroup.tsx +12 -6
  32. package/src/components/dashboard/Tree.tsx +2 -2
  33. package/src/components/ui/accordion.tsx +58 -0
  34. package/src/components/ui/alert-dialog.tsx +139 -0
  35. package/src/components/ui/avatar.tsx +48 -0
  36. package/src/components/ui/button.tsx +56 -0
  37. package/src/components/ui/calendar.tsx +62 -0
  38. package/src/components/ui/card.tsx +86 -0
  39. package/src/components/ui/checkbox.tsx +28 -0
  40. package/src/components/ui/command.tsx +153 -0
  41. package/src/components/ui/dialog.tsx +120 -0
  42. package/src/components/ui/dropdown-menu.tsx +198 -0
  43. package/src/components/ui/form.tsx +177 -0
  44. package/src/components/ui/input.tsx +24 -0
  45. package/src/components/ui/label.tsx +24 -0
  46. package/src/components/ui/popover.tsx +29 -0
  47. package/src/components/ui/progress.tsx +26 -0
  48. package/src/components/ui/radio-group.tsx +42 -0
  49. package/src/components/ui/scroll-area.tsx +51 -0
  50. package/src/components/ui/select.tsx +119 -0
  51. package/src/components/ui/switch.tsx +27 -0
  52. package/src/components/ui/tabs.tsx +53 -0
  53. package/src/components/ui/toggle.tsx +43 -0
  54. package/src/components/ui/tooltip.tsx +28 -0
  55. package/src/components/usePatch.ts +86 -0
  56. package/src/components/useTheme.ts +45 -0
  57. package/src/exports.ts +2 -1
  58. package/src/index.css +96 -60
  59. package/src/lib/IValStore.ts +6 -0
  60. package/src/lib/utils.ts +6 -0
  61. package/src/main.jsx +10 -0
  62. package/src/richtext/conversion/lexicalToRichTextSource.ts +0 -1
  63. package/src/richtext/shadowRootPolyFill.js +115 -0
  64. package/src/server.ts +39 -2
  65. package/src/utils/resolvePath.ts +0 -1
  66. package/src/vite-server.ts +20 -3
  67. package/tailwind.config.js +63 -51
  68. package/tsconfig.json +2 -1
  69. package/vite.config.ts +10 -0
  70. package/src/components/dashboard/ValDashboard.tsx +0 -150
@@ -0,0 +1,115 @@
1
+ // POLYFILL
2
+ // getSelection on shawdowRoot polyfill for safari and firefox
3
+ // https://github.com/GoogleChromeLabs/shadow-selection-polyfill/issues/11
4
+ // https://github.com/GoogleChromeLabs/shadow-selection-polyfill/issues/11#issue-834214496
5
+ // Alternative:
6
+ // https://github.com/codemirror/view/blob/5dfda8ed7929915f63bc82251f2b6229c789c4a4/src/domobserver.ts#L447
7
+
8
+ const SUPPORTS_SHADOW_SELECTION =
9
+ typeof window.ShadowRoot.prototype.getSelection === "function";
10
+ const SUPPORTS_BEFORE_INPUT =
11
+ typeof window.InputEvent.prototype.getTargetRanges === "function";
12
+ const IS_FIREFOX =
13
+ window.navigator.userAgent.toLowerCase().indexOf("firefox") > -1;
14
+
15
+ class ShadowSelection {
16
+ constructor() {
17
+ this._ranges = [];
18
+ }
19
+
20
+ getRangeAt(index) {
21
+ return this._ranges[index];
22
+ }
23
+
24
+ addRange(range) {
25
+ this._ranges.push(range);
26
+ }
27
+
28
+ removeAllRanges() {
29
+ this._ranges = [];
30
+ }
31
+
32
+ // todo: implement remaining `Selection` methods and properties.
33
+ }
34
+
35
+ function getActiveElement() {
36
+ let active = document.activeElement;
37
+
38
+ // eslint-disable-next-line no-constant-condition
39
+ while (true) {
40
+ if (active && active.shadowRoot && active.shadowRoot.activeElement) {
41
+ active = active.shadowRoot.activeElement;
42
+ } else {
43
+ break;
44
+ }
45
+ }
46
+
47
+ return active;
48
+ }
49
+
50
+ if (IS_FIREFOX && !SUPPORTS_SHADOW_SELECTION) {
51
+ window.ShadowRoot.prototype.getSelection = function () {
52
+ return document.getSelection();
53
+ };
54
+ }
55
+
56
+ if (!IS_FIREFOX && !SUPPORTS_SHADOW_SELECTION && SUPPORTS_BEFORE_INPUT) {
57
+ let processing = false;
58
+ let selection = new ShadowSelection();
59
+
60
+ window.ShadowRoot.prototype.getSelection = function () {
61
+ return selection;
62
+ };
63
+
64
+ window.addEventListener(
65
+ "selectionchange",
66
+ () => {
67
+ if (!processing) {
68
+ processing = true;
69
+
70
+ const active = getActiveElement();
71
+
72
+ if (active && active.getAttribute("contenteditable") === "true") {
73
+ //https://stackoverflow.com/questions/60581285/execcommand-is-now-obsolete-whats-the-alternative
74
+ //TLDR: its deprecated but there is not alternative (as of 2023-10-26)
75
+ document.execCommand("indent");
76
+ } else {
77
+ selection.removeAllRanges();
78
+ }
79
+
80
+ processing = false;
81
+ }
82
+ },
83
+ true
84
+ );
85
+
86
+ window.addEventListener(
87
+ "beforeinput",
88
+ (event) => {
89
+ if (processing) {
90
+ const ranges = event.getTargetRanges();
91
+ const range = ranges[0];
92
+
93
+ const newRange = new Range();
94
+
95
+ newRange.setStart(range.startContainer, range.startOffset);
96
+ newRange.setEnd(range.endContainer, range.endOffset);
97
+
98
+ selection.removeAllRanges();
99
+ selection.addRange(newRange);
100
+
101
+ event.preventDefault();
102
+ event.stopImmediatePropagation();
103
+ }
104
+ },
105
+ true
106
+ );
107
+
108
+ window.addEventListener(
109
+ "selectstart",
110
+ () => {
111
+ selection.removeAllRanges();
112
+ },
113
+ true
114
+ );
115
+ }
package/src/server.ts CHANGED
@@ -9,11 +9,14 @@
9
9
 
10
10
  import type { RequestHandler } from "express";
11
11
 
12
+ type Vite = typeof import("vite");
12
13
  export function createRequestHandler(): RequestHandler {
13
14
  if (typeof window === "undefined") {
14
15
  const vite = (async () => {
15
16
  const { fileURLToPath, URL: URL_noresolve } = await import("node:url");
16
- const { createServer } = await import(/* @vite-ignore */ "v" + "ite");
17
+ const { createServer } = await (import(
18
+ /* @vite-ignore */ "v" + "ite"
19
+ ) as Promise<Vite>);
17
20
  const vite = await createServer({
18
21
  root: fileURLToPath(new URL_noresolve("..", import.meta.url)),
19
22
  configFile: fileURLToPath(
@@ -31,8 +34,42 @@ export function createRequestHandler(): RequestHandler {
31
34
  res.statusCode = 200;
32
35
  res.setHeader("Content-Type", "text/css");
33
36
  return res.end(style);
37
+ } else if (req.url.startsWith("/edit")) {
38
+ const html = (await vite).transformIndexHtml(
39
+ req.url,
40
+ `
41
+ <!doctype html>
42
+ <html lang="en">
43
+ <head>
44
+ <meta charset="UTF-8" />
45
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
46
+ <title>Val</title>
47
+ </head>
48
+ <body>
49
+ <div id="root"></div>
50
+ <script type="module" src="/src/main.jsx"></script>
51
+ </body>
52
+ </html>
53
+ `
54
+ );
55
+ return res.end(await html);
34
56
  } else {
35
- return next();
57
+ // TODO: error handling
58
+ try {
59
+ const transformed = await (await vite).transformRequest(req.url);
60
+ if (transformed) {
61
+ const { code, etag } = transformed;
62
+ return res
63
+ .header({ "Content-Type": "application/javascript", Etag: etag })
64
+ .end(code);
65
+ }
66
+ return next();
67
+ } catch (e) {
68
+ if (e instanceof Error) {
69
+ (await vite).ssrFixStacktrace(e);
70
+ }
71
+ return next(e);
72
+ }
36
73
  }
37
74
  };
38
75
  } else {
@@ -13,7 +13,6 @@ export function resolvePath(sourcePath: SourcePath, modules: Modules) {
13
13
  const [moduleId, modulePath] =
14
14
  Internal.splitModuleIdAndModulePath(sourcePath);
15
15
  const valModule = modules[moduleId];
16
- console.log("resolvePath", sourcePath, moduleId, modulePath, modules);
17
16
  if (!valModule?.source) {
18
17
  return result.err({
19
18
  message: `Module "${moduleId}" has no source`,
@@ -1,8 +1,25 @@
1
1
  import type { RequestHandler } from "express";
2
2
 
3
+ const script = "/**REPLACE:SCRIPT*/";
4
+
3
5
  export function createRequestHandler(): RequestHandler {
4
- return (_req, _res, next) => {
5
- // NO-OP
6
- next();
6
+ return (req, res, next) => {
7
+ if (req.url.startsWith("/edit")) {
8
+ res.end(`<!doctype html>
9
+ <html lang="en">
10
+ <head>
11
+ <meta charset="UTF-8" />
12
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
13
+ <title>Val</title>
14
+ <script>${Buffer.from(script, "base64").toString("utf-8")}</script>
15
+ </head>
16
+ <body>
17
+ <div id="root"></div>
18
+ </body>
19
+ </html>
20
+ `);
21
+ } else {
22
+ next();
23
+ }
7
24
  };
8
25
  }
@@ -3,69 +3,81 @@ module.exports = {
3
3
  content: [__dirname + "/src/**/*.{js,ts,jsx,tsx}"],
4
4
  darkMode: ["class", '[data-mode="dark"]'],
5
5
  theme: {
6
+ container: {
7
+ center: true,
8
+ padding: "2rem",
9
+ screens: {
10
+ "2xl": "1400px",
11
+ },
12
+ },
6
13
  zIndex: {
7
14
  hover: 1,
8
15
  window: 2,
9
16
  full: 3,
10
17
  overlay: 4,
18
+ 50: 50,
11
19
  },
12
- colors: {
13
- base: "var(--val-theme-base)",
14
- highlight: "var(--val-theme-highlight)",
15
- border: "var(--val-theme-border)",
16
- fill: "var(--val-theme-fill)",
17
- primary: "var(--val-theme-primary)",
18
- //
19
- white: "#FCFCFC",
20
- "light-gray": "#D6D6D6",
21
- "dark-gray": "#575757",
22
- "medium-black": "#303030",
23
- "warm-black": "#1A1A1A",
24
- yellow: "#FFFF00",
25
- red: "#F02929",
26
- green: "#1CED1C",
27
- },
28
- spacing: {
29
- 0: "0px",
30
- 1: "4px",
31
- 2: "8px",
32
- 3: "12px",
33
- 4: "16px",
34
- },
35
- screens: {
36
- tablet: "640px",
37
- },
20
+
38
21
  fontFamily: {
39
22
  sans: "'Roboto', sans-serif",
40
- serif: "'JetBrains Mono', monospace",
23
+ serif: "'Space Mono', monospace",
41
24
  },
42
- keyframes: {
43
- rotateLeft: {
44
- "0%": { transform: "rotate(0deg)" },
45
- "100%": { transform: "rotate(45deg)" },
25
+ extend: {
26
+ colors: {
27
+ border: "hsl(var(--border))",
28
+ input: "hsl(var(--input))",
29
+ ring: "hsl(var(--ring))",
30
+ background: "hsl(var(--background))",
31
+ foreground: "hsl(var(--foreground))",
32
+ primary: {
33
+ DEFAULT: "hsl(var(--primary))",
34
+ foreground: "hsl(var(--primary-foreground))",
35
+ },
36
+ secondary: {
37
+ DEFAULT: "hsl(var(--secondary))",
38
+ foreground: "hsl(var(--secondary-foreground))",
39
+ },
40
+ destructive: {
41
+ DEFAULT: "hsl(var(--destructive))",
42
+ foreground: "hsl(var(--destructive-foreground))",
43
+ },
44
+ muted: {
45
+ DEFAULT: "hsl(var(--muted))",
46
+ foreground: "hsl(var(--muted-foreground))",
47
+ },
48
+ accent: {
49
+ DEFAULT: "hsl(var(--accent))",
50
+ foreground: "hsl(var(--accent-foreground))",
51
+ },
52
+ popover: {
53
+ DEFAULT: "hsl(var(--popover))",
54
+ foreground: "hsl(var(--popover-foreground))",
55
+ },
56
+ card: {
57
+ DEFAULT: "hsl(var(--card))",
58
+ foreground: "hsl(var(--card-foreground))",
59
+ },
46
60
  },
47
- rotateRight: {
48
- "0%": { transform: "rotate(0deg)" },
49
- "100%": { transform: "rotate(-45deg)" },
61
+ borderRadius: {
62
+ lg: "var(--radius)",
63
+ md: "calc(var(--radius) - 2px)",
64
+ sm: "calc(var(--radius) - 4px)",
50
65
  },
51
- spin: {
52
- "0%": { transform: "rotate(0deg)" },
53
- "100%": { transform: "rotate(360deg)" },
66
+ keyframes: {
67
+ "accordion-down": {
68
+ from: { height: 0 },
69
+ to: { height: "var(--radix-accordion-content-height)" },
70
+ },
71
+ "accordion-up": {
72
+ from: { height: "var(--radix-accordion-content-height)" },
73
+ to: { height: 0 },
74
+ },
75
+ },
76
+ animation: {
77
+ "accordion-down": "accordion-down 0.2s ease-out",
78
+ "accordion-up": "accordion-up 0.2s ease-out",
54
79
  },
55
- },
56
- animation: {
57
- rotateLeft: "rotateLeft 200ms ease-in-out",
58
- rotateRight: "rotateRight 200ms ease-in-out",
59
- spin: "spin 1s linear infinite",
60
- },
61
- transitionProperty: {
62
- opacity: ["opacity"],
63
80
  },
64
81
  },
65
- plugins: [
66
- function ({ addVariant }) {
67
- addVariant("all-but-last-child", "& > *:not(:last-child)");
68
- addVariant("children", "& *");
69
- },
70
- ],
82
+ plugins: [require("tailwindcss-animate")],
71
83
  };
package/tsconfig.json CHANGED
@@ -12,7 +12,8 @@
12
12
  "target": "ES5",
13
13
  "outDir": "dist",
14
14
  "rootDir": "src",
15
- "skipLibCheck": true
15
+ "skipLibCheck": true,
16
+ "baseUrl": "."
16
17
  },
17
18
  "include": ["src/**/*"]
18
19
  }
package/vite.config.ts CHANGED
@@ -1,9 +1,16 @@
1
+ import path from "path";
1
2
  import { defineConfig } from "vite";
2
3
  import react from "@vitejs/plugin-react";
3
4
 
4
5
  // https://vitejs.dev/config/
5
6
  export default defineConfig({
7
+ base: "/api/val/static",
6
8
  plugins: [react()],
9
+ resolve: {
10
+ alias: {
11
+ "@": path.resolve(__dirname, "./src"),
12
+ },
13
+ },
7
14
  build: {
8
15
  lib: {
9
16
  entry: {
@@ -30,4 +37,7 @@ export default defineConfig({
30
37
  external: ["react", "react/jsx-runtime", "react/jsx-dev-runtime"],
31
38
  },
32
39
  },
40
+ optimizeDeps: {
41
+ include: ["react/jsx-runtime", "react/jsx-dev-runtime"],
42
+ },
33
43
  });
@@ -1,150 +0,0 @@
1
- "use client";
2
-
3
- import { SerializedModule } from "@valbuild/core";
4
- import { Json } from "@valbuild/core/src/Json";
5
- import { ValApi } from "@valbuild/core";
6
- import { FC, useEffect, useState } from "react";
7
- import { Dropdown } from "./Dropdown";
8
- import { FormGroup } from "./FormGroup";
9
- import { Grid } from "./Grid";
10
- import { Tree } from "./Tree";
11
-
12
- interface ValDashboardProps {
13
- showDashboard: boolean;
14
- editMode: boolean;
15
- valApi: ValApi;
16
- }
17
- export const ValDashboard: FC<ValDashboardProps> = ({
18
- showDashboard,
19
- editMode,
20
- }) => {
21
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
22
- const [modules, setModules] = useState<SerializedModule[]>([]);
23
- const [selectedPath, setSelectedPath] = useState<string>("");
24
- const [selectedModule, setSelectedModule] = useState<
25
- | {
26
- [key: string]: {
27
- path: string;
28
- type: string;
29
- };
30
- }[]
31
- | undefined
32
- >();
33
-
34
- useEffect(() => {
35
- // valApi.getModules({ patch: true, includeSource: true }).then((modules) => {
36
- // // TODO:
37
- // });
38
- }, []);
39
-
40
- useEffect(() => {
41
- const newModule = modules.find((module) => module.path === selectedPath);
42
- if (newModule) {
43
- const children = mapChildren(newModule);
44
- console.log("children", children);
45
- setSelectedModule(children);
46
- }
47
- }, [selectedPath]);
48
-
49
- const mapChildren = (module: SerializedModule) => {
50
- if (module) {
51
- if (module.schema.type === "array") {
52
- return (module.source as Json[]).map((child) => {
53
- const newModule: {
54
- [key: string]: {
55
- path: string;
56
- type: string;
57
- };
58
- } = {};
59
- if (child) {
60
- for (const key of Object.keys(child)) {
61
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
62
- const type = (module.schema as any).item.items[key]
63
- .type as string;
64
- if (key !== "rank" && type !== "richtext") {
65
- newModule[key] = {
66
- path: key,
67
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
- type: (module.schema as any).item.items[key].type as string,
69
- };
70
- }
71
- }
72
- }
73
- return newModule;
74
- });
75
- } else {
76
- const child = module.source as Json;
77
- const newModule: {
78
- [key: string]: {
79
- path: string;
80
- type: string;
81
- };
82
- } = {};
83
- if (child) {
84
- for (const key of Object.keys(child)) {
85
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
86
- const type = (module.schema as any).items[key].type as string;
87
- if (key !== "rank" && type !== "richtext") {
88
- newModule[key] = {
89
- path: key,
90
- type: type,
91
- };
92
- }
93
- }
94
- }
95
- return [newModule];
96
- }
97
- }
98
- };
99
-
100
- return (
101
- <>
102
- {showDashboard && editMode && (
103
- <div className="bg-base w-screen fixed z-10 top-[68.54px] text-white overflow-hidden">
104
- <Grid>
105
- {modules && (
106
- <Dropdown
107
- options={modules.map((module) => module.path)}
108
- onClick={(path) => setSelectedPath(path)}
109
- />
110
- )}
111
- {selectedModule && (
112
- <Tree>
113
- {selectedModule.map((child, idx) => {
114
- return (
115
- <Tree.Node
116
- key={idx}
117
- path={`Section ${idx + 1}`}
118
- type="section"
119
- >
120
- {Object.values(child).map((value, idx2) => (
121
- <Tree.Node
122
- key={idx2}
123
- path={value.path}
124
- type={value.type as "string" | "image" | "section"}
125
- />
126
- ))}
127
- </Tree.Node>
128
- );
129
- })}
130
- </Tree>
131
- )}
132
- <div className="flex items-center justify-between w-full h-full px-3 font-serif text-xs text-white">
133
- <p>Content</p>
134
- <button className="flex justify-between flex-shrink-0 gap-1">
135
- <span className="w-fit">+</span>
136
- <span className="w-fit">Add item</span>
137
- </button>
138
- </div>
139
- <FormGroup>
140
- <div>test</div>
141
- <div>test</div>
142
- <div>test</div>
143
- </FormGroup>
144
- <div>content</div>
145
- </Grid>
146
- </div>
147
- )}
148
- </>
149
- );
150
- };