diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index 5cfcf1df..b1781c36 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -7,6 +7,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - name: run pre-deploy (build docs and more) + run: | + chmod +x ./scripts/docs-deploy/pre-deploy.sh + ./scripts/docs-deploy/pre-deploy.sh + shell: bash - name: copy docs to gridaco/grida.co uses: DevOpenWRT-Router/github-action-push-to-another-repository@main env: diff --git a/docs/flags/.gitignore b/docs/flags/.gitignore new file mode 100644 index 00000000..a646b350 --- /dev/null +++ b/docs/flags/.gitignore @@ -0,0 +1,7 @@ +## Learn more at scripts/docs-copy/flags-api-docs.js +# custom +----ignore.md +---custom.md + +# flags api docs +--*.md \ No newline at end of file diff --git a/docs/flags/README.md b/docs/flags/README.md new file mode 100644 index 00000000..607c2660 --- /dev/null +++ b/docs/flags/README.md @@ -0,0 +1,5 @@ +# Flags Documentation directory. + +Go to [Index](./index.md) + +## Flags Api references are copied from support-flags package via script - `/scripts/docs-copy/flags-api-docs.sh` diff --git a/docs/flags.md b/docs/flags/index.md similarity index 100% rename from docs/flags.md rename to docs/flags/index.md diff --git a/editor-packages/editor-canvas/README.md b/editor-packages/editor-canvas/README.md new file mode 100644 index 00000000..aa198bad --- /dev/null +++ b/editor-packages/editor-canvas/README.md @@ -0,0 +1 @@ +# A Interactive canvas for runtime frames. diff --git a/editor-packages/editor-canvas/canvas/canvas.tsx b/editor-packages/editor-canvas/canvas/canvas.tsx new file mode 100644 index 00000000..1fb294db --- /dev/null +++ b/editor-packages/editor-canvas/canvas/canvas.tsx @@ -0,0 +1,32 @@ +import React from "react"; +export function Canvas() { + const scale = 1; + return ( +
+ Canvas +
+ ); +} + +function DisableBackdropFilter({ children }: { children: React.ReactNode }) { + return ( +
+ {children} +
+ ); +} diff --git a/packages/support-flags/--export/README.md b/editor-packages/editor-canvas/canvas/index.ts similarity index 100% rename from packages/support-flags/--export/README.md rename to editor-packages/editor-canvas/canvas/index.ts diff --git a/editor-packages/editor-canvas/frame/frame.tsx b/editor-packages/editor-canvas/frame/frame.tsx new file mode 100644 index 00000000..33a44cbd --- /dev/null +++ b/editor-packages/editor-canvas/frame/frame.tsx @@ -0,0 +1,5 @@ +import React from "react"; + +export function Frame() { + return
Frame
; +} diff --git a/editor-packages/editor-canvas/frame/index.ts b/editor-packages/editor-canvas/frame/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/editor-packages/editor-canvas/index.ts b/editor-packages/editor-canvas/index.ts new file mode 100644 index 00000000..3df338f1 --- /dev/null +++ b/editor-packages/editor-canvas/index.ts @@ -0,0 +1 @@ +export const dummy = "canvas"; diff --git a/editor-packages/editor-canvas/nodes/text.tsx b/editor-packages/editor-canvas/nodes/text.tsx new file mode 100644 index 00000000..00175ca1 --- /dev/null +++ b/editor-packages/editor-canvas/nodes/text.tsx @@ -0,0 +1,6 @@ +import React from "react"; +import { ReflectTextNode } from "@design-sdk/figma-node"; + +export function TextNode(text: ReflectTextNode) { + return {text.data}; +} diff --git a/editor-packages/editor-canvas/package.json b/editor-packages/editor-canvas/package.json new file mode 100644 index 00000000..dcaaf5e0 --- /dev/null +++ b/editor-packages/editor-canvas/package.json @@ -0,0 +1,5 @@ +{ + "name": "@code-editor/canvas", + "version": "0.0.0", + "main": "index.ts" +} \ No newline at end of file diff --git a/editor/components/editor/editor-appbar/editor-appbar-fragment-for-code-editor.tsx b/editor/components/editor/editor-appbar/editor-appbar-fragment-for-code-editor.tsx index d4a68e17..c11f393a 100644 --- a/editor/components/editor/editor-appbar/editor-appbar-fragment-for-code-editor.tsx +++ b/editor/components/editor/editor-appbar/editor-appbar-fragment-for-code-editor.tsx @@ -1,25 +1,32 @@ import React from "react"; import styled from "@emotion/styled"; +import { useRouter } from "next/router"; +import { EditorAppbarIconButton } from "./editor-appbar-icon-button"; +import { GithubIcon, NotificationBellIcon } from "icons"; +import { EditorFrameworkConfigOnAppbar } from "../editor-framework-config-on-appbar"; export function AppbarFragmentForCodeEditor() { + const router = useRouter(); + const hasNotification = false; + return ( - - Flutter - - + {/* disable temporarily */} +
+ {/* */} - - + {hasNotification && ( + {}}> + + + )} + { + window.open("https://github.com/gridaco/designto-code/", "_blank"); + }} + > + + ); @@ -41,32 +48,6 @@ const RootWrapperAppbarFragmentForCodeEditor = styled.div` padding-right: 20px; `; -const Frame354 = styled.div` - display: flex; - justify-content: flex-start; - flex-direction: row; - align-items: center; - flex: 1; - gap: 4px; - align-self: stretch; - box-sizing: border-box; -`; - -const Flutter = styled.span` - color: rgba(124, 124, 124, 1); - text-overflow: ellipsis; - font-size: 14px; - font-family: "Helvetica Neue", sans-serif; - font-weight: 400; - text-align: left; -`; - -const IconsAntdSettingOutlined = styled.img` - width: 16px; - height: 16px; - object-fit: cover; -`; - const AppbarActions = styled.div` display: flex; justify-content: center; @@ -74,19 +55,6 @@ const AppbarActions = styled.div` align-items: center; flex: none; gap: 14px; - width: 62px; height: 24px; box-sizing: border-box; `; - -const AppbarIconButton = styled.img` - width: 24px; - height: 24px; - object-fit: cover; -`; - -const AppbarIconButton_0001 = styled.img` - width: 24px; - height: 24px; - object-fit: cover; -`; diff --git a/editor/components/editor/editor-appbar/editor-appbar-icon-button.tsx b/editor/components/editor/editor-appbar/editor-appbar-icon-button.tsx new file mode 100644 index 00000000..1b81abf0 --- /dev/null +++ b/editor/components/editor/editor-appbar/editor-appbar-icon-button.tsx @@ -0,0 +1,13 @@ +import React from "react"; + +const _resting_color = "#787878"; + +export function EditorAppbarIconButton({ + children, + onClick, +}: { + children: React.ReactNode; + onClick: () => void; +}) { + return
{children}
; +} diff --git a/editor/components/editor/editor-framework-config-on-appbar/index.tsx b/editor/components/editor/editor-framework-config-on-appbar/index.tsx new file mode 100644 index 00000000..763bc694 --- /dev/null +++ b/editor/components/editor/editor-framework-config-on-appbar/index.tsx @@ -0,0 +1,32 @@ +import React from "react"; +import styled from "@emotion/styled"; +import { SettingGearOutlinedIcon } from "icons"; + +export function EditorFrameworkConfigOnAppbar() { + return ( + + Flutter + + + ); +} + +const Wrapper = styled.div` + display: flex; + justify-content: flex-start; + flex-direction: row; + align-items: center; + flex: 1; + gap: 4px; + align-self: stretch; + box-sizing: border-box; +`; + +const FrameworkName = styled.span` + color: rgba(124, 124, 124, 1); + text-overflow: ellipsis; + font-size: 14px; + font-family: "Helvetica Neue", sans-serif; + font-weight: 400; + text-align: left; +`; diff --git a/editor/core/reducers/editor-reducer.ts b/editor/core/reducers/editor-reducer.ts index b6ae9fa9..c029251b 100644 --- a/editor/core/reducers/editor-reducer.ts +++ b/editor/core/reducers/editor-reducer.ts @@ -7,6 +7,8 @@ export function editorReducer(state: EditorState, action: Action): EditorState { switch (action.type) { case "select-node": { const { node } = action; + console.clear(); + console.info("cleard console by editorReducer#select-node"); return produce(state, (draft) => { draft.selectedNodes = [node]; }); diff --git a/editor/core/states/editor-state.ts b/editor/core/states/editor-state.ts index e0441504..7d80175e 100644 --- a/editor/core/states/editor-state.ts +++ b/editor/core/states/editor-state.ts @@ -1,4 +1,5 @@ import type { ReflectSceneNode } from "@design-sdk/figma-node"; +import { ComponentNode } from "@design-sdk/figma-types"; import { DesignInput } from "@designto/config/input"; export interface EditorState { @@ -15,7 +16,7 @@ export interface EditorSnapshot { design: FigmaReflectRepository; } -interface FigmaReflectRepository { +export interface FigmaReflectRepository { /** * fileid; filekey */ @@ -23,5 +24,7 @@ interface FigmaReflectRepository { // TODO: pages: { id: string; name: string; children: ReflectSceneNode[] }[]; + components: { [key: string]: ComponentNode }; + // styles: { [key: string]: {} }; input: DesignInput; } diff --git a/editor/core/states/workspace-initial-state.ts b/editor/core/states/workspace-initial-state.ts index a54976be..4e2b652a 100644 --- a/editor/core/states/workspace-initial-state.ts +++ b/editor/core/states/workspace-initial-state.ts @@ -6,14 +6,27 @@ import { } from "./history-initial-state"; import { react_presets, vanilla_presets } from "@grida/builder-config-preset"; +const _IS_DEV = process.env.NODE_ENV === "development"; + +/** + * you can enable this by default on your .env.local file + * + * ```.env.local + * NEXT_PUBLIC_ENABLE_PREVIEW_FEATURE_COMPONENTS_SUPPORT=true + * ``` + */ +const _ENABLE_PREVIEW_FEATURE_COMPONENTS_SUPPORT = + process.env.NEXT_PUBLIC_ENABLE_PREVIEW_FEATURE_COMPONENTS_SUPPORT === "true"; + export function createInitialWorkspaceState( editor: EditorSnapshot ): WorkspaceState { return { history: createInitialHistoryState(editor), preferences: { - debug_mode: false, - enable_preview_feature_components_support: false, + debug_mode: _IS_DEV, + enable_preview_feature_components_support: + _ENABLE_PREVIEW_FEATURE_COMPONENTS_SUPPORT, preview_runner_framework_config: vanilla_presets.vanilla_default, framework_config: react_presets.react_default, }, @@ -24,8 +37,8 @@ export function createPendingWorkspaceState(): WorkspaceState { return { history: createPendingHistoryState(), preferences: { - debug_mode: false, - enable_preview_feature_components_support: false, + debug_mode: null, + enable_preview_feature_components_support: null, preview_runner_framework_config: null, framework_config: null, }, diff --git a/editor/icons/icon-github.tsx b/editor/icons/icon-github.tsx new file mode 100644 index 00000000..9bdee9b1 --- /dev/null +++ b/editor/icons/icon-github.tsx @@ -0,0 +1,19 @@ +import React from "react"; +import { IconProps, width, height } from "./icon-props"; + +export function GithubIcon({ size = 24, color = "black" }: IconProps) { + return ( + + + + ); +} diff --git a/editor/icons/icon-notification-bell.tsx b/editor/icons/icon-notification-bell.tsx new file mode 100644 index 00000000..b1e0cedb --- /dev/null +++ b/editor/icons/icon-notification-bell.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import { IconProps, width, height } from "./icon-props"; + +export function NotificationBellIcon({ + size = 24, + color = "black", +}: IconProps) { + return ( + + + + ); +} diff --git a/editor/icons/icon-props.tsx b/editor/icons/icon-props.tsx new file mode 100644 index 00000000..bb243a84 --- /dev/null +++ b/editor/icons/icon-props.tsx @@ -0,0 +1,20 @@ +type Size = + | number + | { + width: number; + height: number; + }; +export interface IconProps { + size?: Size; + color?: string; +} + +export const width = (size: Size) => flatsize(size, "width"); +export const height = (size: Size) => flatsize(size, "height"); + +const flatsize = (size: Size, porperty: "width" | "height") => { + if (typeof size === "number") { + return size; + } + return size[porperty]; +}; diff --git a/editor/icons/icon-setting-gear-outlined.tsx b/editor/icons/icon-setting-gear-outlined.tsx new file mode 100644 index 00000000..bad0ac82 --- /dev/null +++ b/editor/icons/icon-setting-gear-outlined.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import { IconProps, width, height } from "./icon-props"; + +export function SettingGearOutlinedIcon({ + size = 24, + color = "black", +}: IconProps) { + return ( + + + + ); +} diff --git a/editor/icons/index.ts b/editor/icons/index.ts new file mode 100644 index 00000000..959fa2b1 --- /dev/null +++ b/editor/icons/index.ts @@ -0,0 +1,3 @@ +export * from "./icon-github"; +export * from "./icon-notification-bell"; +export * from "./icon-setting-gear-outlined"; diff --git a/editor/layouts/panel/workspace-bottom-panel-dock-layout.tsx b/editor/layouts/panel/workspace-bottom-panel-dock-layout.tsx index 7c1e53d6..989924ee 100644 --- a/editor/layouts/panel/workspace-bottom-panel-dock-layout.tsx +++ b/editor/layouts/panel/workspace-bottom-panel-dock-layout.tsx @@ -40,9 +40,7 @@ export function WorkspaceBottomPanelDockLayout(props: { } const DockRootWrap = styled.div` - border: solid #d2d2d2; align-self: stretch; - border-width: 1px; display: flex; flex-direction: row; `; diff --git a/editor/package.json b/editor/package.json index 544b2d5e..a498c2ff 100644 --- a/editor/package.json +++ b/editor/package.json @@ -46,6 +46,7 @@ "react": "17.0.1", "react-codemirror2": "^7.2.1", "react-dom": "17.0.1", + "react-hotkeys-hook": "^3.4.4", "react-json-tree": "^0.15.0", "react-resizable": "^3.0.1", "react-spring": "^9.3.2", diff --git a/editor/pages/figma/inspect-component.tsx b/editor/pages/figma/inspect-component.tsx index fe5ea97e..b32be44f 100644 --- a/editor/pages/figma/inspect-component.tsx +++ b/editor/pages/figma/inspect-component.tsx @@ -87,26 +87,22 @@ export default function InspectComponent() {
-
+ {/*
-
-
+
*/} + {/*
-
+
*/}
- {/*
- - -
*/} {/*
diff --git a/editor/pages/files/[key]/[id].tsx b/editor/pages/files/[key]/[id].tsx index e83d17c2..39bc740b 100644 --- a/editor/pages/files/[key]/[id].tsx +++ b/editor/pages/files/[key]/[id].tsx @@ -56,15 +56,8 @@ export default function Page() { if (file) { let val: EditorSnapshot; - const pages = file.document.children.map((page) => ({ - id: page.id, - name: page.name, - children: page["children"]?.map((child) => { - const _mapped = mapper.mapFigmaRemoteToFigma(child); - return convert.intoReflectNode(_mapped); - }), - type: "design", - })); + const components = warmup.componentsFrom(file); + const pages = warmup.pagesFrom(file); if (prevstate) { val = { @@ -102,6 +95,7 @@ export default function Page() { input: null, key: filekey, pages: pages, + components: components, }, selectedPage: warmup.selectedPage(prevstate, pages, null), }; diff --git a/editor/pages/files/[key]/index.tsx b/editor/pages/files/[key]/index.tsx index 4e599068..de184686 100644 --- a/editor/pages/files/[key]/index.tsx +++ b/editor/pages/files/[key]/index.tsx @@ -1,12 +1,11 @@ import React, { useEffect, useCallback, useReducer } from "react"; import { useRouter } from "next/router"; import { SigninToContinueBannerPrmoptProvider } from "components/prompt-banner-signin-to-continue"; -import { Editor } from "scaffolds/editor"; +import { Editor, EditorDefaultProviders } from "scaffolds/editor"; import { EditorSnapshot, StateProvider } from "core/states"; import { WorkspaceAction } from "core/actions"; import { useDesignFile } from "hooks"; -import { convert } from "@design-sdk/figma-node-conversion"; -import { mapper } from "@design-sdk/figma-remote"; + import { warmup } from "scaffolds/editor"; export default function FileEntryEditor() { @@ -31,15 +30,11 @@ export default function FileEntryEditor() { if (file) { let val: EditorSnapshot; - const pages = file.document.children.map((page) => ({ - id: page.id, - name: page.name, - children: page["children"]?.map((child) => { - const _mapped = mapper.mapFigmaRemoteToFigma(child); - return convert.intoReflectNode(_mapped); - }), - type: "design", - })); + // TODO: seed this as well + // ->> file.styles; + + const components = warmup.componentsFrom(file); + const pages = warmup.pagesFrom(file); if (prevstate) { val = { @@ -60,6 +55,8 @@ export default function FileEntryEditor() { selectedLayersOnPreview: [], design: { input: null, + components: components, + // styles: null, key: filekey, pages: pages, }, @@ -75,11 +72,12 @@ export default function FileEntryEditor() { }, [filekey, file?.document?.children]); const safe_value = warmup.safestate(initialState); - return ( - + + + ); diff --git a/editor/pages/to-code/index.tsx b/editor/pages/to-code/index.tsx index 0adaa184..2770eaae 100644 --- a/editor/pages/to-code/index.tsx +++ b/editor/pages/to-code/index.tsx @@ -69,15 +69,8 @@ export default function Page() { if (file) { let val: EditorSnapshot; - const pages = file.document.children.map((page) => ({ - id: page.id, - name: page.name, - children: page["children"]?.map((child) => { - const _mapped = mapper.mapFigmaRemoteToFigma(child); - return convert.intoReflectNode(_mapped); - }), - type: "design", - })); + const components = warmup.componentsFrom(file); + const pages = warmup.pagesFrom(file); if (prevstate) { val = { @@ -115,6 +108,7 @@ export default function Page() { input: null, key: _input.file, pages: pages, + components: components, }, selectedPage: warmup.selectedPage(prevstate, pages, null), }; diff --git a/editor/scaffolds/editor/_providers.tsx b/editor/scaffolds/editor/_providers.tsx new file mode 100644 index 00000000..216d226a --- /dev/null +++ b/editor/scaffolds/editor/_providers.tsx @@ -0,0 +1,40 @@ +import React from "react"; +import { useHotkeys } from "react-hotkeys-hook"; +export function EditorDefaultProviders(props: { children: React.ReactNode }) { + return {props.children}; +} + +function ShortcutsProvider(props: { children: React.ReactNode }) { + const noop = (e) => { + e.preventDefault(); + e.stopPropagation(); + }; + + const _save = keymap("ctrl-cmd", "s"); + const _preferences = keymap("ctrl-cmd", ","); + + useHotkeys(_save.universal, noop); + useHotkeys(_preferences.universal, noop); + + return <>{props.children}; +} + +const keymap = ( + ...c: ("ctrl" | "cmd" | "ctrl-cmd" | "shift" | "a" | "p" | "s" | ",")[] +) => { + const magic_replacer = (s: string, os: "win" | "mac") => { + return replaceAll(s, "ctrl-cmd", os === "win" ? "ctrl" : "cmd"); + }; + + const win = magic_replacer(c.join("+"), "win"); + const mac = magic_replacer(c.join("+"), "mac"); + const universal = [win, mac].join(", "); + return { win, mac, universal }; +}; + +function _escapeRegExp(string) { + return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string +} +function replaceAll(str, match, replacement) { + return str.replace(new RegExp(_escapeRegExp(match), "g"), () => replacement); +} diff --git a/editor/scaffolds/editor/editor.tsx b/editor/scaffolds/editor/editor.tsx index 4a3aff1a..89f3e054 100644 --- a/editor/scaffolds/editor/editor.tsx +++ b/editor/scaffolds/editor/editor.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useRef, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import styled from "@emotion/styled"; import { useRouter } from "next/router"; import { DefaultEditorWorkspaceLayout } from "layouts/default-editor-workspace-layout"; @@ -10,7 +10,7 @@ import { WorkspaceBottomPanelDockLayout } from "layouts/panel/workspace-bottom-p import { CodeEditor } from "components/code-editor"; import { ClearRemoteDesignSessionCache } from "components/clear-remote-design-session-cache"; import { WidgetTree } from "components/visualization/json-visualization/json-tree"; -import { EditorSidebar } from "components/editor"; +import { EditorAppbarFragments, EditorSidebar } from "components/editor"; import { useEditorState, useWorkspaceState } from "core/states"; import { designToCode, Result } from "@designto/code"; import { RemoteImageRepositories } from "@design-sdk/figma-remote/lib/asset-repository/image-repository"; @@ -19,7 +19,6 @@ import { ImageRepository, MainImageRepository, } from "@design-sdk/core/assets-repository"; -import { personal } from "@design-sdk/figma-auth-store"; import { useFigmaAccessToken } from "hooks"; import { get_framework_config } from "query/to-code-options-from-query"; import { CodeOptionsControl } from "components/codeui-code-options-control"; @@ -31,8 +30,8 @@ import { } from "utils/design-query"; import { vanilla_presets } from "@grida/builder-config-preset"; import { EditorSkeleton } from "./skeleton"; -import { MonacoEmptyMock } from "components/code-editor/monaco-mock-empty"; import { colors } from "theme"; +import Link from "next/link"; export function Editor() { const router = useRouter(); @@ -61,7 +60,17 @@ export function Editor() { find_node_by_id_under_inpage_nodes(targetId, thisPageNodes) || null; const root = thisPageNodes - ? container_of_target && DesignInput.fromDesign(container_of_target) + ? container_of_target && + (container_of_target.origin === "COMPONENT" + ? DesignInput.forMasterComponent({ + master: container_of_target, + all: state.design.pages, + components: state.design.components, + }) + : DesignInput.fromDesignWithComponents({ + design: container_of_target, + components: state.design.components, + })) : state.design?.input; const targetted = @@ -129,12 +138,14 @@ export function Editor() { }).then(on_result); // build final code with asset fetch - designToCode({ - input: root, - framework: framework_config, - asset_config: { asset_repository: MainImageRepository.instance }, - build_config: build_config, - }).then(on_result); + if (!MainImageRepository.instance.empty) { + designToCode({ + input: root, + framework: framework_config, + asset_config: { asset_repository: MainImageRepository.instance }, + build_config: build_config, + }).then(on_result); + } } }, [targetted?.id, framework_config?.framework]); @@ -168,12 +179,14 @@ export function Editor() { }, }).then(on_preview_result); - designToCode({ - input: root, - build_config: build_config, - framework: vanilla_presets.vanilla_default, - asset_config: { asset_repository: MainImageRepository.instance }, - }).then(on_preview_result); + if (!MainImageRepository.instance.empty) { + designToCode({ + input: root, + build_config: build_config, + framework: vanilla_presets.vanilla_default, + asset_config: { asset_repository: MainImageRepository.instance }, + }).then(on_preview_result); + } } }, [targetted?.id] @@ -210,7 +223,7 @@ export function Editor() { - {/* */} + {wstate.preferences.debug_mode && ( - -
-
- -
- {(root.entry.origin === "INSTANCE" || - root.entry.origin === "COMPONENT") && ( - - )} -
- -
- -
-
- -
-
+ +
)} @@ -292,6 +276,57 @@ export function Editor() { ); } +const Debugger = ({ + id, + file, + type, + entry, + widget, +}: { + type: string; + id: string; + file: string; + entry: any; + widget: any; +}) => { + const router = useRouter(); + + return ( +
+
+ +
+ {(type === "INSTANCE" || type === "COMPONENT") && ( + + inspect component + + )} +
+ +
+ +
+
+ +
+
+ ); +}; + const CodeEditorContainer = styled.div` display: flex; flex-direction: column; diff --git a/editor/scaffolds/editor/index.ts b/editor/scaffolds/editor/index.ts index c50509a1..a58439cc 100644 --- a/editor/scaffolds/editor/index.ts +++ b/editor/scaffolds/editor/index.ts @@ -1,2 +1,3 @@ export { Editor } from "./editor"; +export { EditorDefaultProviders } from "./_providers"; export * as warmup from "./warmup"; diff --git a/editor/scaffolds/editor/warmup.ts b/editor/scaffolds/editor/warmup.ts index 2b0994a8..906f57bd 100644 --- a/editor/scaffolds/editor/warmup.ts +++ b/editor/scaffolds/editor/warmup.ts @@ -1,6 +1,7 @@ import { createPendingWorkspaceState, EditorSnapshot, + FigmaReflectRepository, WorkspaceState, } from "core/states"; import { createInitialWorkspaceState } from "core/states"; @@ -9,6 +10,10 @@ import { PendingState } from "core/utility-types"; import { DesignInput } from "@designto/config/input"; import { TargetNodeConfig } from "query/target-node"; import { WorkspaceAction } from "core/actions"; +import { FileResponse } from "@design-sdk/figma-remote-types"; +import { convert } from "@design-sdk/figma-node-conversion"; +import { mapper } from "@design-sdk/figma-remote"; +import { find, visit } from "tree-visit"; const pending_workspace_state = createPendingWorkspaceState(); // @@ -38,6 +43,61 @@ export function initialReducer( } } +export function pagesFrom(file: FileResponse): FigmaReflectRepository["pages"] { + return file.document.children.map((page) => ({ + id: page.id, + name: page.name, + children: page["children"]?.map((child) => { + const _mapped = mapper.mapFigmaRemoteToFigma(child); + return convert.intoReflectNode(_mapped); + }), + type: "design", + })); +} + +/** + * only fetch in-file components. components from shared-library (external file) won't be loaded. + * @param file + * @returns + */ +export function componentsFrom( + file: FileResponse +): FigmaReflectRepository["components"] { + const tomap = (a, v) => ({ ...a, [v.id]: v }); + + // only fetch in-file components. components from shared-library (external file) won't be loaded. + const components_in_file = []; + visit<{ id: string; type: string }>(file.document, { + getChildren: (node) => { + if ("children" in node) return node["children"]; + return []; + }, + onEnter: (node) => { + if (node["type"] == "COMPONENT") { + components_in_file.push(node); + } + }, + }); + + // return components_in_file.reduce(tomap, {}); + + return Object.keys(file.components) + .map((k) => { + const id = k; + const meta = file.components[k]; + const master = components_in_file.find((c) => c.id === id); + if (!master) return; + return { + key: meta.key, // only available with api response. the hash key of current version of component for another api call. (not used) + id: master.id, + name: master.name, + ...master, + }; + }) + .filter((c) => c) + .reduce(tomap, {}); +} + export function initializeDesign(design: TargetNodeConfig): EditorSnapshot { return { selectedNodes: [design.node], @@ -45,6 +105,8 @@ export function initializeDesign(design: TargetNodeConfig): EditorSnapshot { selectedPage: null, design: { pages: [], + components: null, + // styles: null, key: design.file, input: DesignInput.fromApiResponse({ ...design, diff --git a/externals/coli b/externals/coli index 83d3cb54..7beb540c 160000 --- a/externals/coli +++ b/externals/coli @@ -1 +1 @@ -Subproject commit 83d3cb546b753859cb23ef2a79346ac438aae563 +Subproject commit 7beb540c885c7b40bf163004a182d60fbdf001ce diff --git a/externals/design-sdk b/externals/design-sdk index 4cf029b7..a958b264 160000 --- a/externals/design-sdk +++ b/externals/design-sdk @@ -1 +1 @@ -Subproject commit 4cf029b74def917122b0d6bed65726c9873cbbf0 +Subproject commit a958b264ac2f37fe67e7b9c5dc3eed047b5756c6 diff --git a/externals/reflect-core b/externals/reflect-core index cc76dca6..f0663b2a 160000 --- a/externals/reflect-core +++ b/externals/reflect-core @@ -1 +1 @@ -Subproject commit cc76dca6a282d323353fbc1fa9d784fa06e949bd +Subproject commit f0663b2aa0b76ace158fef054a67a520b01d94eb diff --git a/packages/builder-config/input/design-input.ts b/packages/builder-config/input/design-input.ts index f2e971b2..e1966a43 100644 --- a/packages/builder-config/input/design-input.ts +++ b/packages/builder-config/input/design-input.ts @@ -1,6 +1,7 @@ import type { ReflectSceneNode } from "@design-sdk/core"; import { mapGrandchildren } from "@design-sdk/core/utils"; import { NodeRepository } from "@design-sdk/figma"; +import type { ComponentNode } from "@design-sdk/figma"; import { RawNodeResponse } from "@design-sdk/figma-remote"; export interface IDesignInput { @@ -35,20 +36,50 @@ export class DesignInput implements IDesignInput { } static fromDesign(design: ReflectSceneNode): DesignInput { - const _allnodes = mapGrandchildren(design, 0, { - includeThis: true, - ignoreGroup: false, + const repository = new NodeRepository({ + // components not supported for `fromdesign` + components: null, + nodes: this._flat_all(design), }); + return new DesignInput({ entry: design, repository: repository }); + } + + static fromDesignWithComponents({ + design, + components, + }: { + design: ReflectSceneNode; + components: { [key: string]: ComponentNode } | ComponentNode[]; + }) { const repository = new NodeRepository({ - // TODO: components not supported for `fromdesign` - components: [], - nodes: [...(_allnodes as any)], + components: Object.values(components), + nodes: this._flat_all(design), }); return new DesignInput({ entry: design, repository: repository }); } + static forMasterComponent({ + all, + master, + components, + }: { + /** + * usually pages. Document#pages + */ + all: { id: string; name: string; children: ReflectSceneNode[] }[]; + master: ReflectSceneNode; + components: { [key: string]: ComponentNode } | ComponentNode[]; + }) { + const repository = new NodeRepository({ + components: Object.values(components), + nodes: all.map((p) => p.children.map(this._flat_all).flat()).flat(), + }); + + return new DesignInput({ entry: master, repository: repository }); + } + static fromApiResponse({ raw, entry, @@ -56,15 +87,17 @@ export class DesignInput implements IDesignInput { raw: RawNodeResponse; entry: ReflectSceneNode; }): DesignInput { - const _allnodes = mapGrandchildren(entry, 0, { - includeThis: true, - ignoreGroup: false, - }); - const repository = new NodeRepository({ components: [...(Object.values(raw.components) as any)], - nodes: [...(_allnodes as any)], + nodes: this._flat_all(entry), }); return new DesignInput({ entry: entry, repository: repository }); } + + private static _flat_all(entry) { + return mapGrandchildren(entry, 0, { + includeThis: true, + ignoreGroup: false, + }); + } } diff --git a/packages/builder-web-core/widget-core/widget-with-style.ts b/packages/builder-web-core/widget-core/widget-with-style.ts index c7fd8b45..1dd1995e 100644 --- a/packages/builder-web-core/widget-core/widget-with-style.ts +++ b/packages/builder-web-core/widget-core/widget-with-style.ts @@ -27,9 +27,14 @@ export abstract class WidgetWithStyle IWHStyleWidget, IPositionedWidget, IBoxShadowWidget, - IEdgeInsetsWidget { - width?: number; - height?: number; + IEdgeInsetsWidget +{ + width?: DimensionLength; + height?: DimensionLength; + minWidth?: DimensionLength; + minHeight?: DimensionLength; + maxWidth?: DimensionLength; + maxHeight?: DimensionLength; constraint?: { left?: DimensionLength; @@ -90,7 +95,8 @@ export abstract class WidgetWithStyle */ export abstract class MultiChildWidgetWithStyle extends WidgetWithStyle - implements IWidgetWithStyle, IMultiChildJsxWidget { + implements IWidgetWithStyle, IMultiChildJsxWidget +{ readonly children: Array = []; constructor({ key }: { key: WidgetKey }) { diff --git a/packages/builder-web-core/widget-instanciation/index.ts b/packages/builder-web-core/widget-instanciation/index.ts index 8a005e53..f615e97e 100644 --- a/packages/builder-web-core/widget-instanciation/index.ts +++ b/packages/builder-web-core/widget-instanciation/index.ts @@ -1,20 +1,53 @@ import { WidgetKey } from "../widget-key"; import { JSXElementConfig, JsxWidget } from "../widget-core"; -import { JSX } from "coli"; +import { JSX, JSXAttribute, Types } from "coli"; export class InstanciationElement extends JsxWidget { readonly identifier: string; - // TODO: support arguments + readonly arguments: { [key: string]: any }; - constructor({ key, identifier }: { key: WidgetKey; identifier: string }) { + constructor({ + key, + identifier, + arguments: _arguments, + }: { + key: WidgetKey; + identifier: string; + arguments: { [key: string]: any }; + }) { super({ key }); this.identifier = identifier; + this.arguments = _arguments ?? {}; } jsxConfig(): JSXElementConfig { + const _attrs = this.makeJsxAttributes(); return { type: "tag-and-attr", tag: JSX.identifier(this.identifier), + attributes: _attrs, }; } + + private makeJsxAttributes() { + const jsxValue = (value: any) => { + switch (typeof value) { + case "undefined": + return JSX.exp(undefined); + case "string": + return JSX.text(value, "template-literal"); + case "number": + return JSX.number(value); + default: + console.error(`Unsupported type of value: ${typeof value}`); + } + }; + + return Object.keys(this.arguments) + .map((key, index) => { + const rec = this.arguments[key]; + return new JSXAttribute(rec.key, jsxValue(rec.value)); + }) + .filter((a) => a); + } } diff --git a/packages/builder-web-core/widgets-native/container/index.ts b/packages/builder-web-core/widgets-native/container/index.ts index 26755c90..2c4a9e41 100644 --- a/packages/builder-web-core/widgets-native/container/index.ts +++ b/packages/builder-web-core/widgets-native/container/index.ts @@ -4,6 +4,7 @@ import { Border, BorderRadiusManifest, BoxShadowManifest, + DimensionLength, } from "@reflect-ui/core"; import { Background } from "@reflect-ui/core/lib/background"; import * as css from "@web-builder/styles"; @@ -21,16 +22,28 @@ export class Container extends StylableJsxWidget { key: WidgetKey; x?: number; y?: number; - width?: number; - height?: number; + + width?: DimensionLength; + height?: DimensionLength; + minWidth?: DimensionLength; + maxWidth?: DimensionLength; + minHeight?: DimensionLength; + maxHeight?: DimensionLength; + background?: Background; borderRadius?: BorderRadiusManifest; boxShadow?: BoxShadowManifest[]; border?: Border; }) { super(p); + this.width = p.width; this.height = p.height; + this.minWidth = p.minWidth; + this.maxWidth = p.maxWidth; + this.minHeight = p.minHeight; + this.maxHeight = p.maxHeight; + this.x = p.x; this.y = p.y; this.background = p.background; @@ -41,8 +54,13 @@ export class Container extends StylableJsxWidget { styleData(): CSSProperties { return { - width: css.px(this.width), - height: css.px(this.height), + width: css.length(this.width), + height: css.length(this.height), + "min-width": css.length(this.minWidth), + "max-width": css.length(this.maxWidth), + "min-height": css.length(this.minHeight), + "max-height": css.length(this.maxHeight), + "box-shadow": css.boxshadow(...(this.boxShadow ?? [])), ...css.background(this.background), ...css.border(this.border), @@ -59,6 +77,7 @@ export class Container extends StylableJsxWidget { export abstract class SelfClosingContainer extends Container - implements Omit { + implements Omit +{ readonly children?: undefined; } diff --git a/packages/builder-web-core/widgets-native/flex/index.ts b/packages/builder-web-core/widgets-native/flex/index.ts index 54aea003..9c4baa2c 100644 --- a/packages/builder-web-core/widgets-native/flex/index.ts +++ b/packages/builder-web-core/widgets-native/flex/index.ts @@ -45,16 +45,24 @@ export class Flex extends MultiChildWidget implements CssMinHeightMixin { borderRadius?: BorderRadiusManifest; border?: Border; + + minWidth?: DimensionLength; + maxWidth?: DimensionLength; minHeight?: DimensionLength; + maxHeight?: DimensionLength; + flexWrap?: FlexWrap; constructor( p: IFlexManifest & { // direction: "row" | "column"; key: WidgetKey; - width?: number; - height?: number; + width?: DimensionLength; + height?: DimensionLength; + minWidth?: DimensionLength; + maxWidth?: DimensionLength; minHeight?: DimensionLength; + maxHeight?: DimensionLength; mainAxisAlignment?: MainAxisAlignment; mainAxisSize?: MainAxisSize; crossAxisAlignment?: CrossAxisAlignment; @@ -74,6 +82,11 @@ export class Flex extends MultiChildWidget implements CssMinHeightMixin { this.width = p.width; this.height = p.height; + this.minWidth = p.minWidth; + this.maxWidth = p.maxWidth; + this.minHeight = p.minHeight; + this.maxHeight = p.maxHeight; + // flex related this.direction = p.direction; this.itemSpacing = p.itemSpacing; @@ -94,7 +107,6 @@ export class Flex extends MultiChildWidget implements CssMinHeightMixin { // css only this.overflow = p.overflow; - this.minHeight = p.minHeight; this.flexWrap = p.flexWrap; } @@ -119,7 +131,12 @@ export class Flex extends MultiChildWidget implements CssMinHeightMixin { ...css.border(this.border), ...css.borderRadius(this.borderRadius), ...flexsizing({ ...this }), - "min-height": css.minHeight(this.minHeight), + + "min-width": css.length(this.minWidth), + "max-width": css.length(this.maxWidth), + "min-height": css.length(this.minHeight), + "max-height": css.length(this.maxHeight), + ...css.background(this.background), "box-sizing": (this.padding && "border-box") || undefined, ...css.padding(this.padding), @@ -146,8 +163,8 @@ function flexsizing({ }: { direction: Axis; mainAxisSize?: MainAxisSize; - width?: number; - height?: number; + width?: DimensionLength; + height?: DimensionLength; flex?: number; }): CSSProperties { switch (mainAxisSize) { @@ -163,8 +180,8 @@ function flexsizing({ case Axis.vertical: return { flex: "none", - width: width && css.px(width), - height: height && css.px(height), + width: width && css.length(width), + height: height && css.length(height), }; } } diff --git a/packages/builder-web-core/widgets-native/html-svg/index.ts b/packages/builder-web-core/widgets-native/html-svg/index.ts index 8709f1d0..76a7def5 100644 --- a/packages/builder-web-core/widgets-native/html-svg/index.ts +++ b/packages/builder-web-core/widgets-native/html-svg/index.ts @@ -1,7 +1,7 @@ import { StylableJsxWidget } from "@web-builder/core/widget-tree/widget"; import { CSSProperties } from "@coli.codes/css"; -import { JSXElementConfig, StylableJSXElementConfig, WidgetKey } from "../.."; -import { px, color } from "@web-builder/styles"; +import { StylableJSXElementConfig, WidgetKey } from "../.."; +import * as css from "@web-builder/styles"; import { JSX, JSXAttribute, @@ -10,7 +10,6 @@ import { JSXIdentifier, JSXOpeningElement, JSXSelfClosingElement, - Snippet, StringLiteral, } from "coli"; import { Color, GradientType } from "@reflect-ui/core"; @@ -110,7 +109,7 @@ export class SvgElement extends StylableJsxWidget { } else { switch (this.fill.type) { case "solid-color": { - return [path_with_fill(color(this.fill as Color))]; + return [path_with_fill(css.color(this.fill as Color))]; } case "graphics": { console.error("graphics fill for svg not supported."); @@ -127,7 +126,7 @@ export class SvgElement extends StylableJsxWidget { new JSXAttribute("offset", new StringLiteral(`${stop}%`)), new JSXAttribute( "style", - new StringLiteral(`stop-color: ${color(c)}`) + new StringLiteral(`stop-color: ${css.color(c)}`) ), ], }); @@ -194,9 +193,9 @@ export class SvgElement extends StylableJsxWidget { }; } return { - width: px(this.width), - height: px(this.height), - color: color(this.color), + width: css.length(this.width), + height: css.length(this.height), + color: css.color(this.color), }; } diff --git a/packages/builder-web-core/widgets-native/html-text-span/index.ts b/packages/builder-web-core/widgets-native/html-text-element/index.ts similarity index 72% rename from packages/builder-web-core/widgets-native/html-text-span/index.ts rename to packages/builder-web-core/widgets-native/html-text-element/index.ts index 88cd91ab..8611aaee 100644 --- a/packages/builder-web-core/widgets-native/html-text-span/index.ts +++ b/packages/builder-web-core/widgets-native/html-text-element/index.ts @@ -1,17 +1,18 @@ -import { JSXElementConfig, StylableJSXElementConfig, WidgetKey } from "../.."; -import { - TextDataWidget, - TextChildWidget, - StylableJsxWidget, -} from "@web-builder/core"; +import { StylableJSXElementConfig, WidgetKey } from "../.."; +import { TextDataWidget, TextChildWidget } from "@web-builder/core"; import * as core from "@reflect-ui/core"; -import { TextOverflow } from "@reflect-ui/core"; +import { TextOverflow, WebTextElement } from "@reflect-ui/core"; import { CSSProperties } from "@coli.codes/css"; import { JSX } from "coli"; import { RGBA } from "@reflect-ui/core"; import * as css from "@web-builder/styles"; import { Dynamic } from "@reflect-ui/core/lib/_utility-types"; +/** + * Html Text Representative. + * + * You can select wich element to render with `elementPreference`. - choose between h1 ~ h6, p, span, etc. + */ export class Text extends TextChildWidget { _type: "Text"; @@ -23,6 +24,9 @@ export class Text extends TextChildWidget { width?: number; height?: number; + // experimental + elementPreference?: WebTextElement; + constructor(p: { key: WidgetKey; data: string; @@ -31,6 +35,7 @@ export class Text extends TextChildWidget { textAlign: core.TextAlign; width?: number; height?: number; + elementPreference?: WebTextElement; }) { super(p); @@ -41,6 +46,9 @@ export class Text extends TextChildWidget { this.textAlign = p.textAlign; this.width = p.width; this.height = p.height; + + // experimental + this.elementPreference = p.elementPreference; } textData() { @@ -54,7 +62,7 @@ export class Text extends TextChildWidget { let textStyle: any = { // text style // ------------------------------------------ - color: css.color((this.textStyle.color as any) as RGBA), + color: css.color(this.textStyle.color as any as RGBA), "text-overflow": this.overflow, "font-size": css.px(this.textStyle.fontSize), "font-family": css.fontFamily(this.textStyle.fontFamily), @@ -78,7 +86,16 @@ export class Text extends TextChildWidget { jsxConfig(): StylableJSXElementConfig { return { type: "tag-and-attr", - tag: JSX.identifier("span"), + tag: JSX.identifier(__get_dedicated_element_tag(this.elementPreference)), }; } } + +const __default_element_tag = "span"; +const __get_dedicated_element_tag = (t?: WebTextElement | undefined) => { + if (t) { + return t; + } else { + return __default_element_tag; + } +}; diff --git a/packages/builder-web-core/widgets-native/index.ts b/packages/builder-web-core/widgets-native/index.ts index 4ab9432e..428e10d6 100644 --- a/packages/builder-web-core/widgets-native/index.ts +++ b/packages/builder-web-core/widgets-native/index.ts @@ -3,7 +3,7 @@ export * from "./container"; export * from "./row"; export * from "./flex"; export * from "./stack"; -export * from "./html-text-span"; +export * from "./html-text-element"; export * from "./html-svg"; export * from "./html-image"; export * from "./error-widget"; diff --git a/packages/builder-web-core/widgets-native/stack/index.ts b/packages/builder-web-core/widgets-native/stack/index.ts index 36c384e2..2315b0d8 100644 --- a/packages/builder-web-core/widgets-native/stack/index.ts +++ b/packages/builder-web-core/widgets-native/stack/index.ts @@ -20,9 +20,13 @@ import { Background } from "@reflect-ui/core/lib/background"; export class Stack extends MultiChildWidget implements CssMinHeightMixin { readonly _type = "stack"; - width: number; - height: number; + width: DimensionLength; + height: DimensionLength; + minWidth?: DimensionLength; + maxWidth?: DimensionLength; minHeight?: DimensionLength; + maxHeight?: DimensionLength; + borderRadius?: BorderRadiusManifest; border?: Border; clipBehavior?: Clip; @@ -30,9 +34,14 @@ export class Stack extends MultiChildWidget implements CssMinHeightMixin { constructor(p: { key: WidgetKey; children: Array; - width: number; - height: number; + + width: DimensionLength; + height: DimensionLength; + minWidth?: DimensionLength; + maxWidth?: DimensionLength; minHeight?: DimensionLength; + maxHeight?: DimensionLength; + boxShadow?: BoxShadowManifest[]; borderRadius?: BorderRadiusManifest; border?: Border; @@ -40,10 +49,13 @@ export class Stack extends MultiChildWidget implements CssMinHeightMixin { clipBehavior?: Clip; }) { super(p); + this.width = p.width; this.height = p.height; - + this.minWidth = p.minWidth; + this.maxWidth = p.maxWidth; this.minHeight = p.minHeight; + this.maxHeight = p.maxHeight; this.background = p.background; this.borderRadius = p.borderRadius; @@ -63,10 +75,13 @@ export class Stack extends MultiChildWidget implements CssMinHeightMixin { styleData(): CSSProperties { return { - width: css.px(this.width), - height: css.px(this.height), + width: css.length(this.width), + height: css.length(this.height), + "min-width": css.length(this.minWidth), + "max-width": css.length(this.maxWidth), + "min-height": css.length(this.minHeight), + "max-height": css.length(this.maxHeight), - "min-height": css.minHeight(this.minHeight), overflow: clip(this.clipBehavior), ...css.background(this.background), ...css.border(this.border), diff --git a/packages/builder-web-react/react-styled-component-widget/from-reusable-widget-tree.ts b/packages/builder-web-react/react-styled-component-widget/from-reusable-widget-tree.ts index 082cfdac..1d1a2811 100644 --- a/packages/builder-web-react/react-styled-component-widget/from-reusable-widget-tree.ts +++ b/packages/builder-web-react/react-styled-component-widget/from-reusable-widget-tree.ts @@ -31,9 +31,9 @@ export function finalizeReactReusable_StyledComponents__Experimental({ }; const token = hanlde(tree); - console.log("token", token); + console.log("from-reusable-widget-tree::token", { token, tree }); const webwi = buildWebWidgetFromTokens(token); - console.log("webwi", webwi); + console.log("from-reusable-widget-tree::web-widget", webwi); const builder = new ReactStyledComponentsBuilder({ entry: webwi, config: { diff --git a/packages/builder-web-react/react-styled-component-widget/react-styled-components-module-builder.ts b/packages/builder-web-react/react-styled-component-widget/react-styled-components-module-builder.ts index aab80cac..deac26bd 100644 --- a/packages/builder-web-react/react-styled-component-widget/react-styled-components-module-builder.ts +++ b/packages/builder-web-react/react-styled-component-widget/react-styled-components-module-builder.ts @@ -106,9 +106,9 @@ export class ReactStyledComponentsBuilder { partDeclarations() { return Array.from(this.styledConfigWidgetMap.keys()) .map((k) => { - return (this.styledConfigWidgetMap.get( - k - ) as StyledComponentJSXElementConfig).styledComponent; + return ( + this.styledConfigWidgetMap.get(k) as StyledComponentJSXElementConfig + ).styledComponent; }) .filter((s) => s); } @@ -170,23 +170,21 @@ export class ReactStyledComponentWidgetModuleExportable { }); file.imports(...this.imports); - console.log("exporting", exporting); + // console.log("exporting", exporting); switch (exporting.type) { case "export-default-anonymous-functional-component": { // exporting.declaration_syntax_choice; // exporting.export_declaration_syntax_choice; // exporting.exporting_position; - const export_default_anaonymous_functional_component = new FunctionDeclaration( - undefined, - { + const export_default_anaonymous_functional_component = + new FunctionDeclaration(undefined, { body: this.body, modifiers: { default: SyntaxKind.DefaultKeyword, export: SyntaxKind.ExportKeyword, }, - } - ); + }); file.declare(export_default_anaonymous_functional_component); file.declare(...this.declarations); break; @@ -219,9 +217,10 @@ export class ReactStyledComponentWidgetModuleExportable { ); break; case "with-declaration": - const _exported_named_function_declaration = add_export_keyword_modifier_to_declaration( - named_function_declaration - ); + const _exported_named_function_declaration = + add_export_keyword_modifier_to_declaration( + named_function_declaration + ); file.declare(_exported_named_function_declaration); file.declare(...this.declarations); break; diff --git a/packages/designto-code/universal/design-to-code.ts b/packages/designto-code/universal/design-to-code.ts index c6ab27c8..93d1ef93 100644 --- a/packages/designto-code/universal/design-to-code.ts +++ b/packages/designto-code/universal/design-to-code.ts @@ -37,13 +37,16 @@ export async function designToCode({ asset_config: AssetsConfig; }): Promise { if (process.env.NODE_ENV === "development") { - console.info( - "dev: starting designtocode with user input", - input, - framework, - build_config, - asset_config - ); + if (framework.framework == "vanilla") { + } else { + console.info( + "dev: starting designtocode with user input", + input, + framework, + build_config, + asset_config + ); + } } // post token processing @@ -72,7 +75,7 @@ export async function designToCode({ console.log("reusable_widget_tree", reusable_widget_tree); // TODO: WIP } catch (_) { - console.error(_); + console.error("error while building reusable widget tree.", _); } } @@ -143,6 +146,12 @@ export async function designToReact({ !input.reusable_widget_tree ) { const reactwidget = toreact.buildReactWidget(input.widget); + if (process.env.NODE_ENV === "development") { + console.info("dev::", "final web token composed", { + input: input.widget, + reactwidget, + }); + } const res = toreact.buildReactApp(reactwidget, react_config); // ------------------------------------------------------------------------ diff --git a/packages/designto-react/app/index.ts b/packages/designto-react/app/index.ts index 0104fcd6..214d0ed6 100644 --- a/packages/designto-react/app/index.ts +++ b/packages/designto-react/app/index.ts @@ -33,7 +33,7 @@ export function buildReactApp( export function buildReactWidget(widget: Widget) { assert( - widget, + widget instanceof Widget, "A valid reflect widget manifest should be passed as an input. none was passed." ); diff --git a/packages/designto-token/config/config.ts b/packages/designto-token/config/config.ts index abf13be3..90e58bf9 100644 --- a/packages/designto-token/config/config.ts +++ b/packages/designto-token/config/config.ts @@ -12,7 +12,7 @@ export interface TokenizerConfig { */ id?: string; - sanitizer_ignore_masking_node: boolean; + sanitizer_ignore_masking_node?: boolean; /** * @default false - flags support enabled by default. diff --git a/packages/designto-token/main.ts b/packages/designto-token/main.ts index aecc5e19..c9a14a00 100644 --- a/packages/designto-token/main.ts +++ b/packages/designto-token/main.ts @@ -163,10 +163,11 @@ function handleNode( } // - button - - const _detect_if_button = detectIf.button(node); - if (_detect_if_button.result) { - return tokenizeButton.fromManifest(node, _detect_if_button.data); - } + // TODO: temporarily disabled - remove comment after button widget is ready + // const _detect_if_button = detectIf.button(node); + // if (_detect_if_button.result) { + // return tokenizeButton.fromManifest(node, _detect_if_button.data); + // } // ------------------------------------------------------------------------- // --------------------------- Detected tokens ----------------------------- diff --git a/packages/designto-token/support-flags/index.ts b/packages/designto-token/support-flags/index.ts index 4125c2da..d0184e63 100644 --- a/packages/designto-token/support-flags/index.ts +++ b/packages/designto-token/support-flags/index.ts @@ -1,14 +1,26 @@ -import { parse } from "@code-features/flags"; +import { + parse, + keys, + FlagsParseResult, + WHDeclarationFlag, + FixWHFlag, +} from "@code-features/flags"; import type { ReflectSceneNode } from "@design-sdk/figma"; import { tokenize_flagged_artwork } from "./token-artwork"; +import { tokenize_flagged_heading } from "./token-heading"; +import { tokenize_flagged_paragraph } from "./token-p"; +import { tokenize_flagged_span } from "./token-span"; import { tokenize_flagged_wrap } from "./token-wrap"; +import { tokenize_flagged_wh_declaration } from "./token-wh"; +import { tokenize_flagged_fix_wh } from "./token-wh-fix"; export default function (node: ReflectSceneNode) { const flags = parse(node.name); return handle_with_flags(node, flags); } -function handle_with_flags(node, flags) { +function handle_with_flags(node, flags: FlagsParseResult) { + // artwork const artwork_flag_alias = flags["artwork"] || flags["export-as"] || @@ -23,8 +35,53 @@ function handle_with_flags(node, flags) { return tokenize_flagged_artwork(node, artwork_flag_alias); } + // wrap const wrap_flag_alias = flags["wrap"] || flags["as-wrap"] || flags["is-wrap"]; if (wrap_flag_alias) { return tokenize_flagged_wrap(node, wrap_flag_alias); } + + // heading + const heading_flag_alias = + flags[keys.flag_key__as_h1] || + flags[keys.flag_key__as_h2] || + flags[keys.flag_key__as_h3] || + flags[keys.flag_key__as_h4] || + flags[keys.flag_key__as_h5] || + flags[keys.flag_key__as_h6]; + + if (heading_flag_alias) { + return tokenize_flagged_heading(node, heading_flag_alias); + } + + const span_flag_alias = flags[keys.flag_key__as_span]; + if (span_flag_alias) { + return tokenize_flagged_span(node, span_flag_alias); + } + + const paragraph_flag_alias = flags[keys.flag_key__as_p]; + if (paragraph_flag_alias) { + return tokenize_flagged_paragraph(node, paragraph_flag_alias); + } + + const wh_declaration_flags: WHDeclarationFlag[] = [ + flags[keys.flag_key__width], + flags[keys.flag_key__min_width], + flags[keys.flag_key__max_width], + flags[keys.flag_key__height], + flags[keys.flag_key__min_height], + flags[keys.flag_key__max_height], + ].filter(Boolean); + + if (wh_declaration_flags.length) { + return tokenize_flagged_wh_declaration(node, wh_declaration_flags); + } + + const fix_wh_flags: FixWHFlag[] = [ + flags[keys.flag_key__fix_width], + flags[keys.flag_key__fix_height], + ].filter(Boolean); + if (fix_wh_flags.length) { + return tokenize_flagged_fix_wh(node, fix_wh_flags); + } } diff --git a/packages/designto-token/support-flags/token-heading/index.ts b/packages/designto-token/support-flags/token-heading/index.ts new file mode 100644 index 00000000..ea22eb3d --- /dev/null +++ b/packages/designto-token/support-flags/token-heading/index.ts @@ -0,0 +1,75 @@ +import { HeadingFlag } from "@code-features/flags"; +import { ReflectSceneNode } from "@design-sdk/figma-node"; +import type { Text } from "@reflect-ui/core"; +import { tokenizeText } from "../../token-text"; + +type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6; + +export function tokenize_flagged_heading( + node: ReflectSceneNode, + flag: HeadingFlag +): Text { + if (flag.value === false) return; + if (node.type !== "TEXT") return; + + const level = get_level(flag); + if (level === undefined) return; // this won't be happening + + const text = tokenizeText.fromText(node); + text.element_preference_experimental = `h${level}`; + return text; +} + +function get_level(flag: HeadingFlag): HeadingLevel { + switch (flag.flag) { + case "h1": + case "as-h1": + case "as-heading1": + case "heading1": + case "as-headline1": + case "headline1": + return 1; + + case "h2": + case "as-h2": + case "as-heading2": + case "heading2": + case "as-headline2": + case "headline2": + return 2; + + case "h3": + case "as-h3": + case "as-heading3": + case "heading3": + case "as-headline3": + case "headline3": + return 3; + + case "h4": + case "as-h4": + case "as-heading4": + case "heading4": + case "as-headline4": + case "headline4": + return 4; + + case "h5": + case "as-h5": + case "as-heading5": + case "heading5": + case "as-headline5": + case "headline5": + return 5; + + case "h6": + case "as-h6": + case "as-heading6": + case "heading6": + case "as-headline6": + case "headline6": + return 6; + } + + console.trace("level parsing from heading flag failed."); +} diff --git a/packages/designto-token/support-flags/token-p/index.ts b/packages/designto-token/support-flags/token-p/index.ts new file mode 100644 index 00000000..ff71ef56 --- /dev/null +++ b/packages/designto-token/support-flags/token-p/index.ts @@ -0,0 +1,16 @@ +import { AsParagraphFlag } from "@code-features/flags"; +import { ReflectSceneNode } from "@design-sdk/figma-node"; +import type { Text } from "@reflect-ui/core"; +import { tokenizeText } from "../../token-text"; + +export function tokenize_flagged_paragraph( + node: ReflectSceneNode, + flag: AsParagraphFlag +): Text { + if (flag.value === false) return; + if (node.type !== "TEXT") return; + + const text = tokenizeText.fromText(node); + text.element_preference_experimental = `p`; + return text; +} diff --git a/packages/designto-token/support-flags/token-span/index.ts b/packages/designto-token/support-flags/token-span/index.ts new file mode 100644 index 00000000..db4b51fd --- /dev/null +++ b/packages/designto-token/support-flags/token-span/index.ts @@ -0,0 +1,16 @@ +import { AsTextSpanFlag } from "@code-features/flags"; +import { ReflectSceneNode } from "@design-sdk/figma-node"; +import type { Text } from "@reflect-ui/core"; +import { tokenizeText } from "../../token-text"; + +export function tokenize_flagged_span( + node: ReflectSceneNode, + flag: AsTextSpanFlag +): Text { + if (flag.value === false) return; + if (node.type !== "TEXT") return; + + const text = tokenizeText.fromText(node); + text.element_preference_experimental = `span`; + return text; +} diff --git a/packages/designto-token/support-flags/token-wh-fix/index.ts b/packages/designto-token/support-flags/token-wh-fix/index.ts new file mode 100644 index 00000000..1c366f34 --- /dev/null +++ b/packages/designto-token/support-flags/token-wh-fix/index.ts @@ -0,0 +1,57 @@ +import { FixWHFlag } from "@code-features/flags"; +import { ReflectSceneNode } from "@design-sdk/figma-node"; +import { IWHStyleWidget, Widget } from "@reflect-ui/core"; +import { tokenize } from "../../main"; +import { unwrappedChild } from "../../wrappings"; + +export function tokenize_flagged_fix_wh( + node: ReflectSceneNode, + flag: FixWHFlag[] +): Widget { + if (!flag.length) return; + const widget = tokenize(node, { + should_ignore_flag: (n) => { + return node.id == n.id; + }, + }); + + const flag_target = unwrappedChild(widget) as IWHStyleWidget; + + const merged = flag + .map((f) => f) + .reduce((a, b) => { + let rec: IWHStyleWidget = {}; + /// + /// NOTE: + /// we are currently fixing the width of the target by specifying all of the current, min, max value. + /// this is not ideal, but it is the best we can do for now. + /// why we are also specifying min, max is because we yet don't have a correct handling system to determine if the current token's value is modified by flag or not. + /// so it is very possible that width/height will be modified, or remvoed by other logic gates. to prevent this, we also specify min, max. + /// + switch (b.flag) { + case "fix-width": + rec["width"] = flag_target.width; + rec["minWidth"] = flag_target.width; + rec["maxWidth"] = flag_target.width; + break; + case "fix-height": + rec["height"] = flag_target.height; + rec["minHeight"] = flag_target.height; + rec["maxHeight"] = flag_target.height; + break; + } + return { + ...a, + ...rec, + }; + }, {}); + + merged.width && (flag_target.width = merged.width); + merged.minWidth && (flag_target.minWidth = merged.minWidth); + merged.maxWidth && (flag_target.maxWidth = merged.maxWidth); + merged.height && (flag_target.height = merged.height); + merged.minHeight && (flag_target.minHeight = merged.minHeight); + merged.maxHeight && (flag_target.maxHeight = merged.maxHeight); + + return widget; +} diff --git a/packages/designto-token/support-flags/token-wh/index.ts b/packages/designto-token/support-flags/token-wh/index.ts new file mode 100644 index 00000000..aa873f71 --- /dev/null +++ b/packages/designto-token/support-flags/token-wh/index.ts @@ -0,0 +1,69 @@ +import { WHDeclarationFlag } from "@code-features/flags"; +import { ReflectSceneNode } from "@design-sdk/figma-node"; +import { + DimensionLength, + isPossibleDimensionLength, + IWHStyleWidget, + Widget, +} from "@reflect-ui/core"; +import { tokenize } from "../../main"; +import { unwrappedChild } from "../../wrappings"; + +export function tokenize_flagged_wh_declaration( + node: ReflectSceneNode, + flag: WHDeclarationFlag[] +): Widget { + if (!flag.length) return; + + const merged = flag + .map((f) => f) + .reduce((a, b) => { + if (isPossibleDimensionLength(b.value)) { + let rec: IWHStyleWidget = {}; + switch (b.flag) { + case "width": + rec["width"] = b.value as DimensionLength; + break; + case "min-width": + rec["minWidth"] = b.value; + break; + case "max-width": + rec["maxWidth"] = b.value; + break; + case "height": + rec["height"] = b.value as DimensionLength; + break; + case "min-height": + rec["minHeight"] = b.value; + break; + case "max-height": + rec["maxHeight"] = b.value; + break; + } + return { + ...a, + ...rec, + }; + } else { + // TODO: support complex values + } + return a; + }, {}); + + const widget = tokenize(node, { + should_ignore_flag: (n) => { + return node.id == n.id; + }, + }); + + const flag_target = unwrappedChild(widget) as IWHStyleWidget; + + merged.width && (flag_target.width = merged.width); + merged.minWidth && (flag_target.minWidth = merged.minWidth); + merged.maxWidth && (flag_target.maxWidth = merged.maxWidth); + merged.height && (flag_target.height = merged.height); + merged.minHeight && (flag_target.minHeight = merged.minHeight); + merged.maxHeight && (flag_target.maxHeight = merged.maxHeight); + + return widget; +} diff --git a/packages/designto-token/support-flags/token-wrap/index.ts b/packages/designto-token/support-flags/token-wrap/index.ts index bc29c8f3..a159e7d5 100644 --- a/packages/designto-token/support-flags/token-wrap/index.ts +++ b/packages/designto-token/support-flags/token-wrap/index.ts @@ -35,7 +35,9 @@ import { unwrappedChild } from "../../wrappings"; export function tokenize_flagged_wrap( node: ReflectSceneNode, flag: AsWrapFlag -) { +): Wrap { + if (flag.value === false) return; + const validated = validate_input(node as any); if (validated.error === false) { // console.log("validated as wrap", validated); @@ -73,9 +75,7 @@ export function tokenize_flagged_wrap( * 2. the children should be columns or rows * @param input */ -function validate_input( - node: ReflectFrameNode -): +function validate_input(node: ReflectFrameNode): | { error: false; wrap_root: ReflectFrameNode; diff --git a/packages/designto-web/tokens-to-web-widget/compose-instanciation.ts b/packages/designto-web/tokens-to-web-widget/compose-instanciation.ts index 40c7bd61..cb9e8671 100644 --- a/packages/designto-web/tokens-to-web-widget/compose-instanciation.ts +++ b/packages/designto-web/tokens-to-web-widget/compose-instanciation.ts @@ -1,18 +1,24 @@ import { Composer } from "."; import * as reusable from "@code-features/component/tokens"; import * as web from "@web-builder/core"; +import { nameit, NameCases } from "coli"; export function compose_instanciation( widget: reusable.InstanceWidget, - child_composer: Composer + child_composer: Composer // not used ) { const masterkey = widget.meta.master.key; + + const identifier = nameit(widget.meta.master.key.originName, { + case: NameCases.pascal, + }).name; + return new web.InstanciationElement({ key: { - name: "foo", - id: masterkey.id, + name: "ExampleUsageOf_" + identifier, // FIXME: should not use identifier as name + id: widget.key.id, }, - // TODO: fix this - identifier: "foo", + identifier: identifier, + arguments: widget.meta.arguments, }); } diff --git a/packages/designto-web/tokens-to-web-widget/compose-wrapped-with-positioned.ts b/packages/designto-web/tokens-to-web-widget/compose-wrapped-with-positioned.ts index bfe612aa..2cc495e6 100644 --- a/packages/designto-web/tokens-to-web-widget/compose-wrapped-with-positioned.ts +++ b/packages/designto-web/tokens-to-web-widget/compose-wrapped-with-positioned.ts @@ -10,8 +10,8 @@ export function compose_wrapped_with_positioned( // ------------------------------------- // override w & h with position provided w/h child.extendStyle({ - width: css.px(widget.width), - height: css.px(widget.height), + width: css.length(widget.width), + height: css.length(widget.height), }); // ------------------------------------- child.constraint = { diff --git a/packages/designto-web/tokens-to-web-widget/index.ts b/packages/designto-web/tokens-to-web-widget/index.ts index b7a10482..959a2c60 100644 --- a/packages/designto-web/tokens-to-web-widget/index.ts +++ b/packages/designto-web/tokens-to-web-widget/index.ts @@ -23,10 +23,6 @@ export function buildWebWidgetFromTokens(widget: core.Widget): JsxWidget { is_root: true, }); - if (process.env.NODE_ENV === "development") { - console.info("dev::", "final web token composed", composed); - } - return composed; } @@ -145,10 +141,12 @@ function compose( else if (widget instanceof core.RenderedText) { thisWebWidget = new web.Text({ ...widget, + key: _key, textStyle: widget.style /** explicit assignment - field name is different */, data: widget.data, - key: _key, + // experimental element specification + elementPreference: widget.element_preference_experimental, }); } else if (widget instanceof core.VectorWidget) { thisWebWidget = new web.SvgElement({ @@ -232,6 +230,10 @@ function compose( // end of logic gate // ------------------------------------- else { + if (thisWebWidget) + throw new Error( + "internal error. this final exception gate should not be entered since there is already a composed widget." + ); // todo - handle case more specific thisWebWidget = new web.ErrorWidget({ key: _key, @@ -239,6 +241,7 @@ function compose( widget.key.originName }" type of "${widget._type}" - ${JSON.stringify(widget.key)}`, }); + console.warn("not handled", widget); } // ------------------------------------- // ------------------------------------- diff --git a/packages/support-components/define.ts b/packages/support-components/define.ts index b0cf4d9e..d7656589 100644 --- a/packages/support-components/define.ts +++ b/packages/support-components/define.ts @@ -1,14 +1,20 @@ -import { Figma, ReflectSceneNode } from "@design-sdk/figma"; +import { ComponentNode, Figma, ReflectSceneNode } from "@design-sdk/figma"; import { compare_instance_with_master, - InstanceDiff, + InstanceDiff_1on1, + MultichildDiff, NodeDiff, } from "@design-sdk/diff"; import { ComponentsUsageRepository } from "./components-usage-repository"; -import { MasterComponentMetaToken } from "./tokens/token-master-component"; +import { + MasterComponentMetaToken, + Property, +} from "./tokens/token-master-component"; import { InstanceMetaToken } from "./tokens/token-instance"; import { keyFromNode } from "@designto/token/key"; - +import { NameCases, nameit, ScopedVariableNamer } from "coli"; +import { ReservedKeywordPlatformPresets } from "@coli.codes/naming/reserved"; +import { visit } from "tree-visit"; type IDMappable = | { [key: string]: T; @@ -23,6 +29,25 @@ function findIn(map: IDMappable, id: string) { } } +function findDeepUnderComponent(component: ComponentNode, id: string) { + let found = null; + visit<{ id; children }>(component, { + getChildren: (node) => { + if ("children" in node) { + return node.children; + } + return []; + }, + onEnter: (node) => { + if (node.id === id) { + found = node; + return "stop"; + } + }, + }); + return found; +} + // based on default strategy // WIP @@ -35,75 +60,155 @@ interface Input { references?: Figma.InstanceNode[]; } -interface Definition { +/** + * A single property definition + */ +interface PropertyDefinition { type: string; + /** + * id of the master + */ master: string; + /** + * id of the instance + */ use: string; + /** + * default value from master + */ default_value: string; + /** + * overrided value from instance + */ overrided_value: string; } +/** + * defines properties as array of PropertyDefinition from whole diff data between master/instance + * @param diff + * @returns + */ +function define_props(diff: NodeDiff): PropertyDefinition[] { + if (!diff.diff) return; + const masterId = diff.ids[0]; + const instanceId = diff.ids[1]; + switch (diff.type) { + case "instance-to-master": + return define_props__instance(diff); + case "text-node": + return [ + diff.characters.diff + ? { + type: "text.data", + default_value: diff.characters.values[0], + overrided_value: diff.characters.values[1], + master: masterId, + use: instanceId, + } + : null, + diff.fills.diff + ? { + type: "text.fill", + default_value: JSON.stringify(diff.fills.values[0]), + overrided_value: JSON.stringify(diff.fills.values[1]), + master: masterId, + use: instanceId, + } + : null, + // TODO: add text styles diff support + ].filter((d) => d); + case "multi-child": + return define_props_multichild(diff); + default: + throw "not handled yet - " + diff["type"]; + } +} + +const define_props_multichild = (diff: MultichildDiff) => { + return diff.values + .map((d) => { + return define_props(d); + }) + .flat() + .filter(Boolean); +}; + +const define_props__instance = (diff: InstanceDiff_1on1) => { + return diff.values + .map((d) => { + return define_props(d); + }) + .flat() + .filter(Boolean); +}; + export function make_instance_component_meta({ entry, components }: Input) { + const propertyNamer = new ScopedVariableNamer( + "property", + ReservedKeywordPlatformPresets.universal + ); + const property_meta = overrided_property_meta({ entry, components }); - const define = (diff: NodeDiff): Definition[] | Definition[][] => { - if (diff.diff) { - const master = diff.ids[0]; - const use = diff.ids[1]; - switch (diff.type) { - case "instance-to-master": - return define_instance(diff); - case "text-node": - return [ - diff.characters.diff - ? { - type: "text.data", - default_value: diff.characters.values[0], - overrided_value: diff.characters.values[1], - master: master, - use: use, - } - : null, - ]; - break; + const masterId = property_meta.ids[0]; + const master = findIn(components, masterId); + + const properties = define_props__instance(property_meta).flat(); + + const __name_cache = {}; + /** + * + * @param propertyOriginId - the origin node of the property will be targetted. e.g. in `master(group(text))`, the master's text's id will be used. + * @returns + */ + const get_property_key = (type: string, propertyOriginId: string) => { + const uid = type + "-" + propertyOriginId; + const originNodeName = findDeepUnderComponent( + master, + propertyOriginId + )?.name; + + if (originNodeName) { + const { name, register } = propertyNamer.nameit(originNodeName, { + case: NameCases.camel, + register: false, + }); + if (__name_cache[uid]) { + return __name_cache[uid]; + } else { + __name_cache[uid] = name; + register(); + return name; } } + throw new Error("origin layer does not contain a valid name"); }; - const define_instance = (diff: InstanceDiff) => { - const definitions = diff.values.map((d) => { - return define(d); - }); - return definitions.filter((d) => d) as any; - }; - - const properties = define_instance(property_meta); - - const master = new MasterComponentMetaToken({ - key: keyFromNode(findIn(components, property_meta.ids[0])), + const masterMeta = new MasterComponentMetaToken({ + key: keyFromNode(findIn(components, masterId)), properties: properties.map((p) => { - return { - key: p.type, + return >{ + key: get_property_key(p.type, p.master), type: p.type, defaultValue: p.default_value, link: { type: "design-link", linksto: { type: "path-property-link", - path: "", - property: p.type, + path: p.type, + property: [{ type: "name", value: p.type }], }, // TODO: }, }; }), - child: findIn(components, property_meta.ids[0]), + child: findIn(components, masterId), }); - const entryInstance = new InstanceMetaToken({ - master: master, + const entryInstanceMeta = new InstanceMetaToken({ + master: masterMeta, key: keyFromNode(entry), arguments: properties.reduce(function (result, item, index, array) { result[item.type] = { - key: item.type, + key: get_property_key(item.type, item.master), value: item.overrided_value, }; return result; @@ -111,8 +216,8 @@ export function make_instance_component_meta({ entry, components }: Input) { }); return new ComponentsUsageRepository({ - components: [master], - usage: { [entry.id]: entryInstance }, + components: [masterMeta], + usage: { [entry.id]: entryInstanceMeta }, }); } @@ -120,17 +225,24 @@ function overrided_property_meta({ entry, components }: Input) { if ( // TODO: needs cleanup "origin" in entry - ? ((entry as any) as ReflectSceneNode).origin !== "INSTANCE" + ? (entry as any as ReflectSceneNode).origin !== "INSTANCE" : entry.type !== "INSTANCE" ) { throw new Error("not a instance"); } const _master = findIn(components, entry.mainComponentId); + if (!_master) + throw new Error( + "cannot find master with `mainComponentId` - id " + + entry.mainComponentId + + `\nIn map provided - length of ${components.length}` + ); + const diff = compare_instance_with_master({ instance: entry, master: _master, components: Array.from(Object.values(components)), }); - // TODO: make meta based on diff + // TODO: make meta based on diff `{ diff, ... }` return diff; } diff --git a/packages/support-components/main.ts b/packages/support-components/main.ts index 14db3876..db965e77 100644 --- a/packages/support-components/main.ts +++ b/packages/support-components/main.ts @@ -37,15 +37,16 @@ export function reusable({ components: repository.components, }); - const components = component_use_repository.components.map( - composeComponentMeta - ); + const components = + component_use_repository.components.map(composeComponentMeta); - return { + const _ = { // asumming root is always a multi child widget tree: composeInstanciationTree(entry, repository, component_use_repository), components: components, }; + // console.log("reusable", _); + return _; } function composeInstanciationTree( @@ -56,15 +57,10 @@ function composeInstanciationTree( widget = unwrappedChild(widget); // unwrap child to reach original input. const { key, _type: _widget_type } = widget; const node = repository.get(key.id); - if (!node) { - console.warn( - "node not found", - key, - repository, - "this is a know issue when trying to find a masking group. this will be fixed in the future." - ); - return; - } + + // prettier-ignore + if (!node) { console.warn("node not found", key, repository, "this is a know issue when trying to find a masking group. this will be fixed in the future."); return; } + if (node.origin === "INSTANCE") { const instanceMeta = componentsUsageRepository.getUsageOf(node.id); const instance = new InstanceWidget({ @@ -77,25 +73,25 @@ function composeInstanciationTree( widget instanceof MultiChildRenderObjectWidget && widget.children.length > 0 ) { - return { - ...widget, - children: widget.children.map((c) => { - return composeInstanciationTree( - c, - repository, - componentsUsageRepository - ); - }), - }; - } else if (widget instanceof SingleChildRenderObjectWidget) { - return { - ...widget, - child: composeInstanciationTree( - widget.child, + // @ts-ignore + widget.children = widget.children.map((c) => { + return composeInstanciationTree( + c, repository, componentsUsageRepository - ), - }; + ); + }); + + return widget; + } else if (widget instanceof SingleChildRenderObjectWidget) { + // @ts-ignore + widget.child = composeInstanciationTree( + widget.child, + repository, + componentsUsageRepository + ); + + return widget; } else { return widget; } @@ -106,9 +102,8 @@ function composeComponentMeta( component: MasterComponentMetaToken ): MasterComponentWidget { const componentNode = component.body as ComponentNode; - const componentTokenizedBody = tokenizeComponent.fromComponentNode( - componentNode - ); + const componentTokenizedBody = + tokenizeComponent.fromComponentNode(componentNode); return new MasterComponentWidget({ key: component.key, diff --git a/packages/support-components/tokens/token-master-component.ts b/packages/support-components/tokens/token-master-component.ts index 950ea86b..7889cd74 100644 --- a/packages/support-components/tokens/token-master-component.ts +++ b/packages/support-components/tokens/token-master-component.ts @@ -46,25 +46,27 @@ export class MasterComponentMetaToken { } } -interface Property { +export interface Property { key: string; type: any; defaultValue: any; link: PropertyLink; } -type PropertyLink = InstanciationPropertyLink | DesignPropertyLink; +export type PropertyLink = + | InstanciationPropertyLink + | DesignPropertyLink; /** * Property link to a instanciation of (another) component. */ -interface InstanciationPropertyLink { +export interface InstanciationPropertyLink { type: "instanciation-link"; master: MasterComponentMetaToken; linksto: Link; } -interface DesignPropertyLink { +export interface DesignPropertyLink { type: "design-link"; /** * path to a property as indexpath. diff --git a/packages/support-flags/--artwork/README.md b/packages/support-flags/--artwork/README.md new file mode 100644 index 00000000..192273c1 --- /dev/null +++ b/packages/support-flags/--artwork/README.md @@ -0,0 +1,40 @@ +## Artwork flag + +**Accepted keys** + +- `--artwork` + +## Syntax + +```ts +`--artwork${"="typeof boolean}` +``` + +## Example + +``` +--artwork + +--artwork=true +--artwork=false + +--artwork=yes +--artwork=no + +----artwork +``` + +## Behavior + +**Interpreter** + +When applied, this will force the node to be exported as an image. + +**Render** + +- HTML: rendered as an `` element. +- Flutter: rendered as an `Image` widget. + +## See Also + +- [`--export-as`](../--export-as) diff --git a/packages/support-flags/--as-br/README.md b/packages/support-flags/--as-br/README.md index 840be114..b6054b02 100644 --- a/packages/support-flags/--as-br/README.md +++ b/packages/support-flags/--as-br/README.md @@ -1,3 +1,17 @@ -# As Break +--- +stage: + - proposal + - draft +--- + + + + + +# (Draft) `--as-br` As Break ## Interprets the flagged element as break (`
`). + +## When to use + +While designing a text layout, or a paragraph, we tend to use autolayout (a flex layout) to contain multiple text to be visually displayed as single text. diff --git a/packages/support-flags/--as-char/README.md b/packages/support-flags/--as-char/README.md new file mode 100644 index 00000000..659c2e40 --- /dev/null +++ b/packages/support-flags/--as-char/README.md @@ -0,0 +1,18 @@ +# As Character + +## See also + +- `--as-space` +- `--as-text-group` + +## When to use + +Some characters can't be displayed in design tool with only using pure text. + +Special characters, custom emoji, custom image, etc. + +## Example + +``` +--as-char="🤔" +``` diff --git a/packages/support-flags/--as-h1/README.md b/packages/support-flags/--as-h1/README.md index e69de29b..e5cd9812 100644 --- a/packages/support-flags/--as-h1/README.md +++ b/packages/support-flags/--as-h1/README.md @@ -0,0 +1,55 @@ +--- +stage: + - production + - staging + - experimental +--- + +# `--as-h1` As Heading1 + +**Accepted keys** + +- `--as-h1` +- `--as-heading1` +- `--as-headline1` +- `--h1` +- `--heading1` +- `--headline1` + +## Syntax + +```ts +`--h1${"="typeof boolean}` +``` + +## Example + +``` +--h1 + +--h1=true +--h1=false + +--h1=yes +--h1=no + +----h1 +``` + +## Behavior + +**Element** +When applied, this will force the node to be rendered as a `

` element. + +**Text style** +We don't yet support text style matching with `--h1` flag. + +## See Also + +- [`--as-h2`](../--as-h2) +- [`--as-h3`](../--as-h3) +- [`--as-h4`](../--as-h4) +- [`--as-h5`](../--as-h5) +- [`--as-h6`](../--as-h6) +- [`--as-p`](../--as-p) +- [`--as-br`](../--as-br) diff --git a/packages/support-flags/--as-h1/index.ts b/packages/support-flags/--as-h1/index.ts new file mode 100644 index 00000000..e0ec0afb --- /dev/null +++ b/packages/support-flags/--as-h1/index.ts @@ -0,0 +1,30 @@ +// primary +export const flag_key__as_h1 = "as-h1"; +// alias +const flag_key__as_heading1 = "as-heading1"; +const flag_key__as_headline1 = "as-headline1"; +const flag_key__h1 = "h1"; +const flag_key__heading1 = "heading1"; +const flag_key__headline1 = "headline1"; + +export const flag_key_alias__as_h1 = [ + flag_key__as_h1, + flag_key__as_heading1, + flag_key__as_headline1, + flag_key__h1, + flag_key__heading1, + flag_key__headline1, +]; + +export interface AsHeading1Flag { + flag: + | typeof flag_key__as_h1 + | typeof flag_key__as_heading1 + | typeof flag_key__as_headline1 + | typeof flag_key__h1 + | typeof flag_key__heading1 + | typeof flag_key__headline1; + + value: boolean; + _raw?: string; +} diff --git a/packages/support-flags/--as-h2/README.md b/packages/support-flags/--as-h2/README.md index e69de29b..9bd9cc32 100644 --- a/packages/support-flags/--as-h2/README.md +++ b/packages/support-flags/--as-h2/README.md @@ -0,0 +1,55 @@ +--- +stage: + - production + - staging + - experimental +--- + +# `--as-h2` As Heading2 + +**Accepted keys** + +- `--as-h2` +- `--as-heading2` +- `--as-headline2` +- `--h2` +- `--heading2` +- `--headline2` + +## Syntax + +```ts +`--h2${"="typeof boolean}` +``` + +## Example + +``` +--h2 + +--h2=true +--h2=false + +--h2=yes +--h2=no + +----h2 +``` + +## Behavior + +**Element** +When applied, this will force the node to be rendered as a `

` element. + +**Text style** +We don't yet support text style matching with `--h2` flag. + +## See Also + +- [`--as-h1`](../--as-h1) +- [`--as-h3`](../--as-h3) +- [`--as-h4`](../--as-h4) +- [`--as-h5`](../--as-h5) +- [`--as-h6`](../--as-h6) +- [`--as-p`](../--as-p) +- [`--as-br`](../--as-br) diff --git a/packages/support-flags/--as-h2/index.ts b/packages/support-flags/--as-h2/index.ts new file mode 100644 index 00000000..ecb238db --- /dev/null +++ b/packages/support-flags/--as-h2/index.ts @@ -0,0 +1,30 @@ +// primary +export const flag_key__as_h2 = "as-h2"; +// alias +const flag_key__as_heading2 = "as-heading2"; +const flag_key__as_headline2 = "as-headline2"; +const flag_key__h2 = "h2"; +const flag_key__heading2 = "heading2"; +const flag_key__headline2 = "headline2"; + +export const flag_key_alias__as_h2 = [ + flag_key__as_h2, + flag_key__as_heading2, + flag_key__as_headline2, + flag_key__h2, + flag_key__heading2, + flag_key__headline2, +]; + +export interface AsHeading2Flag { + flag: + | typeof flag_key__as_h2 + | typeof flag_key__as_heading2 + | typeof flag_key__as_headline2 + | typeof flag_key__h2 + | typeof flag_key__heading2 + | typeof flag_key__headline2; + + value: boolean; + _raw?: string; +} diff --git a/packages/support-flags/--as-h3/README.md b/packages/support-flags/--as-h3/README.md index e69de29b..8adfd930 100644 --- a/packages/support-flags/--as-h3/README.md +++ b/packages/support-flags/--as-h3/README.md @@ -0,0 +1,55 @@ +--- +stage: + - production + - staging + - experimental +--- + +# `--as-h3` As Heading3 + +**Accepted keys** + +- `--as-h3` +- `--as-heading3` +- `--as-headline3` +- `--h3` +- `--heading3` +- `--headline3` + +## Syntax + +```ts +`--h3${"="typeof boolean}` +``` + +## Example + +``` +--h3 + +--h3=true +--h3=false + +--h3=yes +--h3=no + +----h3 +``` + +## Behavior + +**Element** +When applied, this will force the node to be rendered as a `

` element. + +**Text style** +We don't yet support text style matching with `--h3` flag. + +## See Also + +- [`--as-h1`](../--as-h1) +- [`--as-h2`](../--as-h2) +- [`--as-h4`](../--as-h4) +- [`--as-h5`](../--as-h5) +- [`--as-h6`](../--as-h6) +- [`--as-p`](../--as-p) +- [`--as-br`](../--as-br) diff --git a/packages/support-flags/--as-h3/index.ts b/packages/support-flags/--as-h3/index.ts new file mode 100644 index 00000000..96e53acf --- /dev/null +++ b/packages/support-flags/--as-h3/index.ts @@ -0,0 +1,30 @@ +// primary +export const flag_key__as_h3 = "as-h3"; +// alias +const flag_key__as_heading3 = "as-heading3"; +const flag_key__as_headline3 = "as-headline3"; +const flag_key__h3 = "h3"; +const flag_key__heading3 = "heading3"; +const flag_key__headline3 = "headline3"; + +export const flag_key_alias__as_h3 = [ + flag_key__as_h3, + flag_key__as_heading3, + flag_key__as_headline3, + flag_key__h3, + flag_key__heading3, + flag_key__headline3, +]; + +export interface AsHeading3Flag { + flag: + | typeof flag_key__as_h3 + | typeof flag_key__as_heading3 + | typeof flag_key__as_headline3 + | typeof flag_key__h3 + | typeof flag_key__heading3 + | typeof flag_key__headline3; + + value: boolean; + _raw?: string; +} diff --git a/packages/support-flags/--as-h4/README.md b/packages/support-flags/--as-h4/README.md index e69de29b..bbae2a70 100644 --- a/packages/support-flags/--as-h4/README.md +++ b/packages/support-flags/--as-h4/README.md @@ -0,0 +1,55 @@ +--- +stage: + - production + - staging + - experimental +--- + +# `--as-h4` As Heading4 + +**Accepted keys** + +- `--as-h4` +- `--as-heading4` +- `--as-headline4` +- `--h4` +- `--heading4` +- `--headline4` + +## Syntax + +```ts +`--h4${"="typeof boolean}` +``` + +## Example + +``` +--h4 + +--h4=true +--h4=false + +--h4=yes +--h4=no + +----h4 +``` + +## Behavior + +**Element** +When applied, this will force the node to be rendered as a `

` element. + +**Text style** +We don't yet support text style matching with `--h4` flag. + +## See Also + +- [`--as-h1`](../--as-h1) +- [`--as-h2`](../--as-h2) +- [`--as-h3`](../--as-h3) +- [`--as-h5`](../--as-h5) +- [`--as-h6`](../--as-h6) +- [`--as-p`](../--as-p) +- [`--as-br`](../--as-br) diff --git a/packages/support-flags/--as-h4/index.ts b/packages/support-flags/--as-h4/index.ts new file mode 100644 index 00000000..7e76d91e --- /dev/null +++ b/packages/support-flags/--as-h4/index.ts @@ -0,0 +1,30 @@ +// primary +export const flag_key__as_h4 = "as-h4"; +// alias +const flag_key__as_heading4 = "as-heading4"; +const flag_key__as_headline4 = "as-headline4"; +const flag_key__h4 = "h4"; +const flag_key__heading4 = "heading4"; +const flag_key__headline4 = "headline4"; + +export const flag_key_alias__as_h4 = [ + flag_key__as_h4, + flag_key__as_heading4, + flag_key__as_headline4, + flag_key__h4, + flag_key__heading4, + flag_key__headline4, +]; + +export interface AsHeading4Flag { + flag: + | typeof flag_key__as_h4 + | typeof flag_key__as_heading4 + | typeof flag_key__as_headline4 + | typeof flag_key__h4 + | typeof flag_key__heading4 + | typeof flag_key__headline4; + + value: boolean; + _raw?: string; +} diff --git a/packages/support-flags/--as-h5/README.md b/packages/support-flags/--as-h5/README.md index e69de29b..5503a80a 100644 --- a/packages/support-flags/--as-h5/README.md +++ b/packages/support-flags/--as-h5/README.md @@ -0,0 +1,55 @@ +--- +stage: + - production + - staging + - experimental +--- + +# `--as-h5` As Heading5 + +**Accepted keys** + +- `--as-h5` +- `--as-heading5` +- `--as-headline5` +- `--h5` +- `--heading5` +- `--headline5` + +## Syntax + +```ts +`--h5${"="typeof boolean}` +``` + +## Example + +``` +--h5 + +--h5=true +--h5=false + +--h5=yes +--h5=no + +----h5 +``` + +## Behavior + +**Element** +When applied, this will force the node to be rendered as a `

` element. + +**Text style** +We don't yet support text style matching with `--h5` flag. + +## See Also + +- [`--as-h1`](../--as-h1) +- [`--as-h2`](../--as-h2) +- [`--as-h3`](../--as-h3) +- [`--as-h4`](../--as-h4) +- [`--as-h6`](../--as-h6) +- [`--as-p`](../--as-p) +- [`--as-br`](../--as-br) diff --git a/packages/support-flags/--as-h5/index.ts b/packages/support-flags/--as-h5/index.ts new file mode 100644 index 00000000..34d1e7ef --- /dev/null +++ b/packages/support-flags/--as-h5/index.ts @@ -0,0 +1,30 @@ +// primary +export const flag_key__as_h5 = "as-h5"; +// alias +const flag_key__as_heading5 = "as-heading5"; +const flag_key__as_headline5 = "as-headline5"; +const flag_key__h5 = "h5"; +const flag_key__heading5 = "heading5"; +const flag_key__headline5 = "headline5"; + +export const flag_key_alias__as_h5 = [ + flag_key__as_h5, + flag_key__as_heading5, + flag_key__as_headline5, + flag_key__h5, + flag_key__heading5, + flag_key__headline5, +]; + +export interface AsHeading5Flag { + flag: + | typeof flag_key__as_h5 + | typeof flag_key__as_heading5 + | typeof flag_key__as_headline5 + | typeof flag_key__h5 + | typeof flag_key__heading5 + | typeof flag_key__headline5; + + value: boolean; + _raw?: string; +} diff --git a/packages/support-flags/--as-h6/README.md b/packages/support-flags/--as-h6/README.md index e69de29b..3db14248 100644 --- a/packages/support-flags/--as-h6/README.md +++ b/packages/support-flags/--as-h6/README.md @@ -0,0 +1,55 @@ +--- +stage: + - production + - staging + - experimental +--- + +# `--as-h6` As Heading6 + +**Accepted keys** + +- `--as-h6` +- `--as-heading6` +- `--as-headline6` +- `--h6` +- `--heading6` +- `--headline6` + +## Syntax + +```ts +`--h6${"="typeof boolean}` +``` + +## Example + +``` +--h6 + +--h6=true +--h6=false + +--h6=yes +--h6=no + +----h6 +``` + +## Behavior + +**Element** +When applied, this will force the node to be rendered as a `
` element. + +**Text style** +We don't yet support text style matching with `--h6` flag. + +## See Also + +- [`--as-h1`](../--as-h1) +- [`--as-h2`](../--as-h2) +- [`--as-h3`](../--as-h3) +- [`--as-h4`](../--as-h4) +- [`--as-h5`](../--as-h5) +- [`--as-p`](../--as-p) +- [`--as-br`](../--as-br) diff --git a/packages/support-flags/--as-h6/index.ts b/packages/support-flags/--as-h6/index.ts new file mode 100644 index 00000000..e70c8949 --- /dev/null +++ b/packages/support-flags/--as-h6/index.ts @@ -0,0 +1,30 @@ +// primary +export const flag_key__as_h6 = "as-h6"; +// alias +const flag_key__as_heading6 = "as-heading6"; +const flag_key__as_headline6 = "as-headline6"; +const flag_key__h6 = "h6"; +const flag_key__heading6 = "heading6"; +const flag_key__headline6 = "headline6"; + +export const flag_key_alias__as_h6 = [ + flag_key__as_h6, + flag_key__as_heading6, + flag_key__as_headline6, + flag_key__h6, + flag_key__heading6, + flag_key__headline6, +]; + +export interface AsHeading6Flag { + flag: + | typeof flag_key__as_h6 + | typeof flag_key__as_heading6 + | typeof flag_key__as_headline6 + | typeof flag_key__h6 + | typeof flag_key__heading6 + | typeof flag_key__headline6; + + value: boolean; + _raw?: string; +} diff --git a/packages/support-flags/--as-nbsp/README.md b/packages/support-flags/--as-nbsp/README.md new file mode 100644 index 00000000..b0630ed3 --- /dev/null +++ b/packages/support-flags/--as-nbsp/README.md @@ -0,0 +1,36 @@ +# `--as-nbsp` As Non-Breaking Space + +**Accepted keys** + +- `--as-nbsp` +- `--as-space` +- `--nbsp` + +**`--as-nbsp` Extends `--as-char`** + +> `--as-nbsp` is equivalent to `--as-char= ` or `--as-char=" "` + +## Syntax + +```ts +`--nbsp${"="typeof number}` +``` + +## Example + +``` +--nbsp // 1 space, with `" "` on html +--as-nbsp // 1 space, with `" "` on html +--as-space // 1 space, with `" "` on html, if not available, e.g. on trailing, uses `" "`. + + +--nbsp=2 // 2 spaces +--nbsp=3 // 3 spaces + + +----nbsp=2 // commented out +``` + +## See also + +- [`--as-char`](../--as-char) diff --git a/packages/support-flags/--as-p/README.md b/packages/support-flags/--as-p/README.md index e69de29b..c33c2aca 100644 --- a/packages/support-flags/--as-p/README.md +++ b/packages/support-flags/--as-p/README.md @@ -0,0 +1,48 @@ +# `--as-p` As Paragraph (Text) + +> This flag is for web platform. Otherwise, it will be ignored, have no impact on the final output. + +**Accepted keys** + +- `--as-p` +- `--as-paragraph` +- `--paragraph` + +## Syntax + +```ts +`--as-p${"="typeof boolean}` +``` + +## Example + +``` +--paragraph + +--paragraph=true +--paragraph=false + +--paragraph=yes +--paragraph=no + +----paragraph +``` + +## Behavior + +**Element** +When applied, this will force the node to be rendered as a `

` element. + +**Text style** +We don't yet support text style matching with `--p` flag. + +## See Also + +- [`--as-h1`](../--as-h1) +- [`--as-h2`](../--as-h2) +- [`--as-h3`](../--as-h3) +- [`--as-h4`](../--as-h4) +- [`--as-h5`](../--as-h5) +- [`--as-h6`](../--as-h6) +- [`--as-p`](../--as-p) +- [`--as-br`](../--as-br) diff --git a/packages/support-flags/--as-p/index.ts b/packages/support-flags/--as-p/index.ts new file mode 100644 index 00000000..5157b7cb --- /dev/null +++ b/packages/support-flags/--as-p/index.ts @@ -0,0 +1,21 @@ +// priamry +export const flag_key__as_p = "as-p"; +// alias +const flag_key__as_paragraph = "as-paragraph"; +const flag_key__paragraph = "paragraph"; + +export const flag_key_alias__as_p = [ + flag_key__as_p, + flag_key__as_paragraph, + flag_key__paragraph, +]; + +export interface AsParagraphFlag { + flag: + | typeof flag_key__as_p + | typeof flag_key__as_paragraph + | typeof flag_key__paragraph; + + value: boolean; + _raw?: string; +} diff --git a/packages/support-flags/--as-span/README.md b/packages/support-flags/--as-span/README.md new file mode 100644 index 00000000..85325de1 --- /dev/null +++ b/packages/support-flags/--as-span/README.md @@ -0,0 +1,59 @@ +# `--as-span` As TextSpan (Text) + +> This flag is for web platform. Otherwise, it will be ignored, have no impact on the final output. + +**Accepted keys** + +- `--as-span` +- `--as-text-span` +- `--as-textspan` +- `--text-span` +- `--textspan` + +## Syntax + +```ts +`--as-span${"="typeof boolean}` +``` + +## Example + +``` +--as-span + +--as-span=true +--as-span=false + +--as-span=yes +--as-span=no + +----as-span +``` + +## Behavior + +**Element** +When applied, this will force the node to be rendered as a `` element. + +**Text style** +We don't yet support text style matching with `--as-span` flag. + +## Anatomy + +```ts +export interface AsSpanFlag { + flag: "as-span" | "as-text-span" | "as-textspan" | "text-span" | "textspan"; + + value?: boolean; +} +``` + +## See Also + +- [`--as-h1`](../--as-h1) +- [`--as-h2`](../--as-h2) +- [`--as-h3`](../--as-h3) +- [`--as-h4`](../--as-h4) +- [`--as-h5`](../--as-h5) +- [`--as-h6`](../--as-h6) +- [`--as-p`](../--as-p) diff --git a/packages/support-flags/--as-span/index.ts b/packages/support-flags/--as-span/index.ts new file mode 100644 index 00000000..4ccad77c --- /dev/null +++ b/packages/support-flags/--as-span/index.ts @@ -0,0 +1,27 @@ +// primary +export const flag_key__as_span = "as-span"; +// alias +const flag_key__as_text_span = "as-text-span"; +const flag_key__as_textspan = "as-textspan"; +const flag_key__text_span = "text-span"; +const flag_key__textspan = "textspan"; + +export const flag_key_alias__as_span = [ + flag_key__as_span, + flag_key__as_text_span, + flag_key__as_textspan, + flag_key__text_span, + flag_key__textspan, +]; + +export interface AsTextSpanFlag { + flag: + | typeof flag_key__as_span + | typeof flag_key__as_text_span + | typeof flag_key__as_textspan + | typeof flag_key__text_span + | typeof flag_key__textspan; + + value: boolean; + _raw?: string; +} diff --git a/packages/support-flags/--as-text-group/README.md b/packages/support-flags/--as-text-group/README.md new file mode 100644 index 00000000..69332d0e --- /dev/null +++ b/packages/support-flags/--as-text-group/README.md @@ -0,0 +1,3 @@ +## (Draft) Text group + +While creating a multilined, complex text, for example, inserting a custom emoji, or a custom image, we need to use a text group. This is usually done by using a autolayout or col-row oriented multiple autolayouts. diff --git a/packages/support-flags/--exact-height/README.md b/packages/support-flags/--exact-height/README.md deleted file mode 100644 index 26cdc8a5..00000000 --- a/packages/support-flags/--exact-height/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# `--exact-height` Flag (Draft) - -When applied, this will force dedicated layer's height to be ignore responsive height, use current height as fixed height instead. - -## Syntax - -```ts -`--exact-height${"="typeof boolean}` -``` - -## Example - -``` ---exact-height - ---exact-height=true ---exact-height=false - ---exact-height=True ---exact-height=False - ---exact-height=yes ---exact-height=no -``` diff --git a/packages/support-flags/--exact-width/README.md b/packages/support-flags/--exact-width/README.md deleted file mode 100644 index 1f2c3d00..00000000 --- a/packages/support-flags/--exact-width/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# `--exact-width` Flag (Draft) - -When applied, this will force dedicated layer's `width` to be ignore responsive width, use current width as fixed width instead. - -## Syntax - -```ts -`--exact-width${"="typeof boolean}` -``` - -## Example - -``` ---exact-width - ---exact-width=true ---exact-width=false - ---exact-width=True ---exact-width=False - ---exact-width=yes ---exact-width=no -``` diff --git a/packages/support-flags/--export-as/README.md b/packages/support-flags/--export-as/README.md new file mode 100644 index 00000000..e892b75d --- /dev/null +++ b/packages/support-flags/--export-as/README.md @@ -0,0 +1,6 @@ +# Export As + +**Accepted keys** + +- `--export-as` +- `--export` diff --git a/packages/support-flags/--fix-bottom/README.md b/packages/support-flags/--fix-bottom/README.md new file mode 100644 index 00000000..216594a6 --- /dev/null +++ b/packages/support-flags/--fix-bottom/README.md @@ -0,0 +1,7 @@ +# `--fix-bottom` Flag (Draft) + +When applied, this will force dedicated layer's bottom to be ignore responsive bottom, use current bottom as fixed bottom instead. + +> ⚠️ This is different from `--position-fixed`. In general, this flag is not used for designing a footer. + +## See also diff --git a/packages/support-flags/--fix-height/README.md b/packages/support-flags/--fix-height/README.md new file mode 100644 index 00000000..e6e13e7a --- /dev/null +++ b/packages/support-flags/--fix-height/README.md @@ -0,0 +1,24 @@ +# `--fix-height` Flag (Draft) + +When applied, this will force dedicated layer's `height` to be ignore responsive `height`, use current `height` as fixed `height` instead. + +## Syntax + +```ts +`--fix-height${"="typeof boolean}` +``` + +## Example + +``` +--fix-height + +--fix-height=true +--fix-height=false + +--fix-height=True +--fix-height=False + +--fix-height=yes +--fix-height=no +``` diff --git a/packages/support-flags/--fix-height/index.ts b/packages/support-flags/--fix-height/index.ts new file mode 100644 index 00000000..72a8d0c5 --- /dev/null +++ b/packages/support-flags/--fix-height/index.ts @@ -0,0 +1,10 @@ +export const flag_key__fix_height = "fix-height"; + +export const flag_key_alias__fix_height = [flag_key__fix_height]; + +export interface FixHeightFlag { + flag: typeof flag_key__fix_height; + + value: true; + _raw?: string; +} diff --git a/packages/support-flags/--fix-width/README.md b/packages/support-flags/--fix-width/README.md new file mode 100644 index 00000000..5e7b57f9 --- /dev/null +++ b/packages/support-flags/--fix-width/README.md @@ -0,0 +1,28 @@ +# `--fix-width` Flag (Draft) + +When applied, this will force dedicated layer's `width` to be ignore responsive width, use current width as fixed width instead. + +**Accepted keys** + +- `--fix-width` + +## Syntax + +```ts +`--fix-width${"="typeof boolean}` +``` + +## Example + +``` +--fix-width + +--fix-width=true +--fix-width=false + +--fix-width=True +--fix-width=False + +--fix-width=yes +--fix-width=no +``` diff --git a/packages/support-flags/--fix-width/index.ts b/packages/support-flags/--fix-width/index.ts new file mode 100644 index 00000000..1d8d23c8 --- /dev/null +++ b/packages/support-flags/--fix-width/index.ts @@ -0,0 +1,9 @@ +export const flag_key__fix_width = "fix-width"; + +export const flag_key_alias__fix_width = [flag_key__fix_width]; +export interface FixWidthFlag { + flag: typeof flag_key__fix_width; + + value: true; + _raw?: string; +} diff --git a/packages/support-flags/--height/README.md b/packages/support-flags/--height/README.md new file mode 100644 index 00000000..275732a6 --- /dev/null +++ b/packages/support-flags/--height/README.md @@ -0,0 +1,117 @@ +# `--height` for explicit height + +**Accepted keys** + +- `--height` +- `--h` + +## Syntax + +```ts +`--height=${typeof length}`; +``` + +## Example + +``` +--height=100 +--h=100 + +--height=100px + +--height=100vh +``` + +## Behavior + +**Element** +There is no impact on element itself, but it can break relative layouts. + +**Style** +When applied, this will force the node to be rendered with a `height` style. + +## (Proposal) (Draft) Advanced use + +> This feature is a proposal, won't work on production use. + +height and height support specifying min, max, and initial values. + +This is how it looks like: + +`--height=(initial)` or `--height=(min)-(max)-(initial)` + +``` +--height=100px // initial only +--height=100px-1080px-50vh // min, max, initial +--height=100px-1080px // min, max +--height=?-1080px // min (none), max +--height=100px-? // min , max (none) +--height=100px-?-50vh // min , max (none), initial +``` + +you can skip the declaration with `?` keyword. this is a special keyword, interpreted as `undefined` + +"`--height=100px-?-50vh`" this will make css for example, as below. + +```css +.foo { + min-height: 100px; + height: 50vh; +} +``` + +"`--height=100px-1080px`" this indicates only min and max, yet, still the height will be specified based on current height of the origin design. + +```css +.foo { + min-height: 100px; + max-height: 1080px; + height: 400px; /* this is from the design */ +} +``` + +### Ignoring one of the property + +**Using explicit `--ignore` flag** +to ignore the height, you can use new flag `--ignore` set to height. like so - `--ignore=height` + +```css +.foo { + min-height: 100px; + max-height: 1080px; + /* height: 400px; this is ignored by --ignore flag */ +} +``` + +**Using `?` keyword** +Otherwise, you can simply use silincer keyword `?` to ignore the height, like so - `--height=100px-1080px-?` + +This will also generate style like below. + +```css +.foo { + min-height: 100px; + max-height: 1080px; + /* height: 400px; this is ignored by "?" keyword */ +} +``` + +### Referencing current value with `.` + +Setting max-height to current height (based on design) with - `--height=100px-.-.` + +This will generate style like below. + +```css +.foo { + min-height: 100px; + max-height: 400px; /* the current height from design */ + height: 400px; /* the current height from design */ +} +``` + +## See Also + +- [`--max-height`](../--max-height) +- [`--min-height`](../--min-height) +- [`--width`](../--width) diff --git a/packages/support-flags/--height/index.ts b/packages/support-flags/--height/index.ts new file mode 100644 index 00000000..52409ceb --- /dev/null +++ b/packages/support-flags/--height/index.ts @@ -0,0 +1,18 @@ +import { DimensionLength } from "@reflect-ui/core"; + +export const flag_key__height = "height"; + +export const flag_key_alias__height = [flag_key__height]; + +export interface HeightFlag { + flag: typeof flag_key__height; + + value: + | DimensionLength + | { + min?: DimensionLength; + max?: DimensionLength; + initial?: DimensionLength; + }; + _raw?: string; +} diff --git a/packages/support-flags/--max-height/README.md b/packages/support-flags/--max-height/README.md new file mode 100644 index 00000000..c5a2f735 --- /dev/null +++ b/packages/support-flags/--max-height/README.md @@ -0,0 +1,36 @@ +# Max height + +**Accepted keys** + +- `--max-height` +- `--maxheight` + +## Syntax + +```ts +`--max-height=${typeof length}`; +``` + +## Example + +``` +--max-height=100 + +--max-height=100px + +--max-height=100vh +``` + +## Behavior + +**Element** +There is no impact on element itself, but it can break relative layouts. + +**Style** +When applied, this will force the node to be rendered with a `max-height` style. + +## See Also + +- [`--max-width`](../--max-width) +- [`--min-height`](../--max-height) +- [`--height`](../--height) diff --git a/packages/support-flags/--max-height/index.ts b/packages/support-flags/--max-height/index.ts new file mode 100644 index 00000000..c5bf888a --- /dev/null +++ b/packages/support-flags/--max-height/index.ts @@ -0,0 +1,18 @@ +import { DimensionLength } from "@reflect-ui/core"; + +// primary +export const flag_key__max_height = "max-height"; +// alias +const flag_key__maxheight = "maxheight"; + +export const flag_key_alias__max_height = [ + flag_key__max_height, + flag_key__maxheight, +]; + +export interface MaxHeightFlag { + flag: typeof flag_key__max_height | typeof flag_key__maxheight; + + value: DimensionLength; + _raw?: string; +} diff --git a/packages/support-flags/--max-width/README.md b/packages/support-flags/--max-width/README.md new file mode 100644 index 00000000..6dd58b83 --- /dev/null +++ b/packages/support-flags/--max-width/README.md @@ -0,0 +1,36 @@ +# Max width + +**Accepted keys** + +- `--max-width` +- `--maxwidth` + +## Syntax + +```ts +`--max-width=${typeof length}`; +``` + +## Example + +``` +--max-width=100 + +--max-width=100px + +--max-width=400vw +``` + +## Behavior + +**Element** +There is no impact on element itself, but it can break relative layouts. + +**Style** +When applied, this will force the node to be rendered with a max-width style. + +## See Also + +- [`--max-height`](../--max-height) +- [`--min-width`](../--max-width) +- [`--width`](../--max-width) diff --git a/packages/support-flags/--max-width/index.ts b/packages/support-flags/--max-width/index.ts new file mode 100644 index 00000000..00b8afa1 --- /dev/null +++ b/packages/support-flags/--max-width/index.ts @@ -0,0 +1,18 @@ +import { DimensionLength } from "@reflect-ui/core"; + +// primary +export const flag_key__max_width = "max-width"; +// alias +const flag_key__maxwidth = "maxwidth"; + +export const flag_key_alias__max_width = [ + flag_key__max_width, + flag_key__maxwidth, +]; + +export interface MaxWidthFlag { + flag: typeof flag_key__max_width | typeof flag_key__maxwidth; + + value: DimensionLength; + _raw?: string; +} diff --git a/packages/support-flags/--min-height/README.md b/packages/support-flags/--min-height/README.md new file mode 100644 index 00000000..059d7771 --- /dev/null +++ b/packages/support-flags/--min-height/README.md @@ -0,0 +1,36 @@ +# Min height + +**Accepted keys** + +- `--min-height` +- `--minheight` + +## Syntax + +```ts +`--min-height=${typeof length}`; +``` + +## Example + +``` +--min-height=100 + +--min-height=100px + +--min-height=100vh +``` + +## Behavior + +**Element** +There is no impact on element itself, but it can break relative layouts. + +**Style** +When applied, this will force the node to be rendered with a `min-height` style. + +## See Also + +- [`--max-height`](../--max-height) +- [`--min-width`](../--max-width) +- [`--height`](../--height) diff --git a/packages/support-flags/--min-height/index.ts b/packages/support-flags/--min-height/index.ts new file mode 100644 index 00000000..e8b59db3 --- /dev/null +++ b/packages/support-flags/--min-height/index.ts @@ -0,0 +1,18 @@ +import { DimensionLength } from "@reflect-ui/core"; + +// primary +export const flag_key__min_height = "min-height"; +// alias +const flag_key__minheight = "minheight"; + +export const flag_key_alias__min_height = [ + flag_key__min_height, + flag_key__minheight, +]; + +export interface MinHeightFlag { + flag: typeof flag_key__min_height | typeof flag_key__minheight; + + value: DimensionLength; + _raw?: string; +} diff --git a/packages/support-flags/--min-width/README.md b/packages/support-flags/--min-width/README.md new file mode 100644 index 00000000..28df8815 --- /dev/null +++ b/packages/support-flags/--min-width/README.md @@ -0,0 +1,36 @@ +# Min width + +**Accepted keys** + +- `--min-width` +- `--minwidth` + +## Syntax + +```ts +`--min-width=${typeof length}`; +``` + +## Example + +``` +--min-width=100 + +--min-width=100px + +--min-width=100vw +``` + +## Behavior + +**Element** +There is no impact on element itself, but it can break relative layouts. + +**Style** +When applied, this will force the node to be rendered with a `min-width` style. + +## See Also + +- [`--max-width`](../--max-width) +- [`--min-height`](../--min-height) +- [`--width`](../--width) diff --git a/packages/support-flags/--min-width/index.ts b/packages/support-flags/--min-width/index.ts new file mode 100644 index 00000000..bd5be19c --- /dev/null +++ b/packages/support-flags/--min-width/index.ts @@ -0,0 +1,18 @@ +import { DimensionLength } from "@reflect-ui/core"; + +// primary +export const flag_key__min_width = "min-width"; +// alias +const flag_key__minwidth = "minwidth"; + +export const flag_key_alias__min_width = [ + flag_key__min_width, + flag_key__minwidth, +]; + +export interface MinWidthFlag { + flag: typeof flag_key__min_width | typeof flag_key__minwidth; + + value: DimensionLength; + _raw?: string; +} diff --git a/packages/support-flags/--position-sticky/README.md b/packages/support-flags/--position-sticky/README.md new file mode 100644 index 00000000..79ed0a42 --- /dev/null +++ b/packages/support-flags/--position-sticky/README.md @@ -0,0 +1 @@ +# (WIP) Position as Sticky. diff --git a/packages/support-flags/--width/README.md b/packages/support-flags/--width/README.md new file mode 100644 index 00000000..40beb9cb --- /dev/null +++ b/packages/support-flags/--width/README.md @@ -0,0 +1,117 @@ +# `--width` for explicit width + +**Accepted keys** + +- `--width` +- `--w` + +## Syntax + +```ts +`--width=${typeof length}`; +``` + +## Example + +``` +--width=100 +--w=100 + +--width=100px + +--width=400vw +``` + +## Behavior + +**Element** +There is no impact on element itself, but it can break relative layouts. + +**Style** +When applied, this will force the node to be rendered with a `width` style. + +## (Proposal) (Draft) Advanced use + +> This feature is a proposal, won't work on production use. + +width and height support specifying min, max, and initial values. + +This is how it looks like: + +`--width=(initial)` or `--width=(min)-(max)-(initial)` + +``` +--width=100px // initial only +--width=100px-1080px-50vw // min, max, initial +--width=100px-1080px // min, max +--width=?-1080px // min (none), max +--width=100px-? // min , max (none) +--width=100px-?-50vw // min , max (none), initial +``` + +you can skip the declaration with `?` keyword. this is a special keyword, interpreted as `undefined` + +"`--width=100px-?-50vw`" this will make css for example, as below. + +```css +.foo { + min-width: 100px; + width: 50vw; +} +``` + +"`--width=100px-1080px`" this indicates only min and max, yet, still the width will be specified based on current width of the origin design. + +```css +.foo { + min-width: 100px; + max-width: 1080px; + width: 400px; /* this is from the design */ +} +``` + +### Ignoring one of the property + +**Using explicit `--ignore` flag** +to ignore the width, you can use new flag `--ignore` set to width. like so - `--ignore=width` + +```css +.foo { + min-width: 100px; + max-width: 1080px; + /* width: 400px; this is ignored by --ignore flag */ +} +``` + +**Using `?` keyword** +Otherwise, you can simply use silincer keyword `?` to ignore the width, like so - `--width=100px-1080px-?` + +This will also generate style like below. + +```css +.foo { + min-width: 100px; + max-width: 1080px; + /* width: 400px; this is ignored by "?" keyword */ +} +``` + +### Referencing current value with `.` + +Setting max-width to current width (based on design) with - `--width=100px-.-.` + +This will generate style like below. + +```css +.foo { + min-width: 100px; + max-width: 400px; /* the current width from design */ + width: 400px; /* the current width from design */ +} +``` + +## See Also + +- [`--max-width`](../--max-width) +- [`--min-width`](../--min-width) +- [`--height`](../--height) diff --git a/packages/support-flags/--width/index.ts b/packages/support-flags/--width/index.ts new file mode 100644 index 00000000..44b9fabb --- /dev/null +++ b/packages/support-flags/--width/index.ts @@ -0,0 +1,18 @@ +import { DimensionLength } from "@reflect-ui/core"; + +export const flag_key__width = "width"; + +export const flag_key_alias__width = [flag_key__width]; + +export interface WidthFlag { + flag: typeof flag_key__width; + + value: + | DimensionLength + | { + min?: DimensionLength; + max?: DimensionLength; + initial?: DimensionLength; + }; + _raw?: string; +} diff --git a/packages/support-flags/index.ts b/packages/support-flags/index.ts index a1d48997..85d4a3ef 100644 --- a/packages/support-flags/index.ts +++ b/packages/support-flags/index.ts @@ -1,29 +1,4 @@ -import { parse as parseflags } from "@design-sdk/flags/parsing-strategy-dashdash"; - -import { flag_key__artwork } from "./--artwork"; -import { flag_key__as_wrap } from "./--as-wrap"; -import { flag_key__module } from "./--module"; export * from "./types"; - -export function parse(name: string) { - try { - return parseflags(name, [ - { - name: flag_key__artwork, - type: "bool", - }, - { - name: flag_key__as_wrap, - type: "bool", - }, - { - name: flag_key__module, - type: "bool", // TODO: support string also. - }, - //. TODO: add other flags under here. - ]); - } catch (_) { - console.error("error while parsing flags", _); - return {}; - } -} +export * as keys from "./keys"; +export { parse } from "./parse"; +export type { FlagsParseResult } from "./parse"; diff --git a/packages/support-flags/keys.ts b/packages/support-flags/keys.ts new file mode 100644 index 00000000..3761cb8e --- /dev/null +++ b/packages/support-flags/keys.ts @@ -0,0 +1,61 @@ +import { flag_key_alias__as_h1, flag_key__as_h1 } from "./--as-h1"; +import { flag_key_alias__as_h2, flag_key__as_h2 } from "./--as-h2"; +import { flag_key_alias__as_h3, flag_key__as_h3 } from "./--as-h3"; +import { flag_key_alias__as_h4, flag_key__as_h4 } from "./--as-h4"; +import { flag_key_alias__as_h5, flag_key__as_h5 } from "./--as-h5"; +import { flag_key_alias__as_h6, flag_key__as_h6 } from "./--as-h6"; +import { flag_key_alias__as_p, flag_key__as_p } from "./--as-p"; +import { flag_key_alias__as_span, flag_key__as_span } from "./--as-span"; + +import { flag_key_alias__width, flag_key__width } from "./--width"; +import { flag_key_alias__min_width, flag_key__min_width } from "./--min-width"; +import { flag_key_alias__max_width, flag_key__max_width } from "./--max-width"; + +import { flag_key_alias__height, flag_key__height } from "./--height"; +// prettier-ignore +import { flag_key_alias__min_height, flag_key__min_height } from "./--min-height"; +// prettier-ignore +import { flag_key_alias__max_height, flag_key__max_height } from "./--max-height"; +import { flag_key_alias__fix_width, flag_key__fix_width } from "./--fix-width"; +// prettier-ignore +import { flag_key_alias__fix_height, flag_key__fix_height } from "./--fix-height"; + +export { + flag_key__as_h1, + flag_key__as_h2, + flag_key__as_h3, + flag_key__as_h4, + flag_key__as_h5, + flag_key__as_h6, +}; +export { flag_key__as_p }; +export { flag_key__as_span }; + +export { + flag_key__width, + flag_key__min_width, + flag_key__max_width, + flag_key__height, + flag_key__min_height, + flag_key__max_height, +}; +export { flag_key__fix_width, flag_key__fix_height }; + +export const alias = { + as_h1: flag_key_alias__as_h1, + as_h2: flag_key_alias__as_h2, + as_h3: flag_key_alias__as_h3, + as_h4: flag_key_alias__as_h4, + as_h5: flag_key_alias__as_h5, + as_h6: flag_key_alias__as_h6, + as_p: flag_key_alias__as_p, + as_span: flag_key_alias__as_span, + width: flag_key_alias__width, + min_width: flag_key_alias__min_width, + max_width: flag_key_alias__max_width, + height: flag_key_alias__height, + min_height: flag_key_alias__min_height, + max_height: flag_key_alias__max_height, + fix_width: flag_key_alias__fix_width, + fix_height: flag_key_alias__fix_height, +}; diff --git a/packages/support-flags/parse.ts b/packages/support-flags/parse.ts new file mode 100644 index 00000000..319fc834 --- /dev/null +++ b/packages/support-flags/parse.ts @@ -0,0 +1,301 @@ +import { + parse as parseflags, + Option, + Results, +} from "@design-sdk/flags/parsing-strategy-dashdash"; + +import * as keys from "./keys"; + +import { flag_key__artwork } from "./--artwork"; +import { flag_key__as_wrap } from "./--as-wrap"; +import { flag_key__module } from "./--module"; +import type { + HeadingFlag, + TextElementPreferenceFlag, + AsParagraphFlag, + AsTextSpanFlag, + SimpleBooleanValueFlag, + FixWHFlag, + WHDeclarationFlag, +} from "./types"; + +export type FlagsParseResult = Results & { + __meta: { + [key: string]: boolean; + }; +}; + +export function parse(name: string): FlagsParseResult { + try { + const _raw_parsed = parseflags(name, [ + { + name: flag_key__artwork, + type: "bool", + }, + { + name: flag_key__as_wrap, + type: "bool", + }, + //#region + __h1_alias_pref, + __h2_alias_pref, + __h3_alias_pref, + __h4_alias_pref, + __h5_alias_pref, + __h6_alias_pref, + __p_alias_pref, + __textspan_alias_pref, + //#endregion + + //#region + __width_alias_pref, + __max_width_alias_pref, + __min_width_alias_pref, + __height_alias_pref, + __max_height_alias_pref, + __min_height_alias_pref, + //#endregion + + //#region + __fix_width_alias_pref, + __fix_height_alias_pref, + //#endregion + + { + name: flag_key__module, + type: "bool", // TODO: support string also. + }, + //. TODO: add other flags under here. + ]); + + const as_heading_flag = transform_heading_alias_from_raw(_raw_parsed); + const as_paragraph_flag = handle_single_boolean_flag_alias( + _raw_parsed, + keys.alias.as_p + ); + const as_span_flag = handle_single_boolean_flag_alias( + _raw_parsed, + keys.alias.as_span + ); + + const wh_declaration_flag = + transform_wh_declaration_alias_from_raw(_raw_parsed); + const fix_wh_flag = handle_single_boolean_flag_alias( + _raw_parsed, + [...keys.alias.fix_width, ...keys.alias.fix_height] + ); + // console.log("_raw_parsed", _raw_parsed); + + return { + ..._raw_parsed, + ...as_heading_flag, + ...(as_paragraph_flag ?? {}), + ...(as_span_flag ?? {}), + ...(wh_declaration_flag ?? {}), + ...(fix_wh_flag ?? {}), + __meta: { + contains_heading_flag: notempty(as_heading_flag), + contains_paragraph_flag: notempty(as_paragraph_flag), + contains_span_flag: notempty(as_span_flag), + contains_wh_declaration_flag: notempty(as_span_flag), + contains_fix_wh_flag: notempty(fix_wh_flag), + }, + }; + } catch (_) { + // TODO: this can happen when unregistered flag is used. this will be fixed. + console.error("error while parsing flags", _); + return {} as any; + } +} + +const notempty = (obj) => Object.keys(obj ?? {}).length > 0; + +const _simple_boolean_value_flag_prefernce_mapper = ( + k: string | Array +): Option => + Array.isArray(k) + ? { + names: k, + type: "bool", + } + : { + name: k, + type: "bool", + }; + +function handle_single_boolean_flag_alias( + raw: { [key: string]: boolean }, + alias: string[] +) { + // e.g. `[ { h1: true } ]` + const mapped: { key: string; value: boolean }[] = alias.map((_) => ({ + key: _, + value: raw[_], + })); + + // e.g `{ h1: {flag: "h1", value: true} }` + const converted: { [key: string]: T } = mapped.reduce((acc, c, i) => { + if (raw[c.key]) { + return { + ...acc, + [c.key]: { flag: c.key, value: raw[c.key], _raw: String(raw) }, + }; + } + return acc; + }, {}); + + if (Object.keys(converted).length > 0) return converted; +} + +function transform_heading_alias_from_raw(raw: { [key: string]: boolean }): { + [key: string]: HeadingFlag; +} { + const _h1_alias = handle_single_boolean_flag_alias( + raw, + keys.alias.as_h1 + ); + if (_h1_alias) return _h1_alias; + + const _h2_alias = handle_single_boolean_flag_alias( + raw, + keys.alias.as_h2 + ); + if (_h2_alias) return _h2_alias; + + const _h3_alias = handle_single_boolean_flag_alias( + raw, + keys.alias.as_h3 + ); + if (_h3_alias) return _h3_alias; + + const _h4_alias = handle_single_boolean_flag_alias( + raw, + keys.alias.as_h4 + ); + if (_h4_alias) return _h4_alias; + + const _h5_alias = handle_single_boolean_flag_alias( + raw, + keys.alias.as_h5 + ); + if (_h5_alias) return _h5_alias; + + const _h6_alias = handle_single_boolean_flag_alias( + raw, + keys.alias.as_h6 + ); + if (_h6_alias) return _h6_alias; +} + +const __h1_alias_pref = _simple_boolean_value_flag_prefernce_mapper( + keys.alias.as_h1 +); + +const __h2_alias_pref = _simple_boolean_value_flag_prefernce_mapper( + keys.alias.as_h2 +); + +const __h3_alias_pref = _simple_boolean_value_flag_prefernce_mapper( + keys.alias.as_h3 +); + +const __h4_alias_pref = _simple_boolean_value_flag_prefernce_mapper( + keys.alias.as_h4 +); + +const __h5_alias_pref = _simple_boolean_value_flag_prefernce_mapper( + keys.alias.as_h5 +); + +const __h6_alias_pref = _simple_boolean_value_flag_prefernce_mapper( + keys.alias.as_h6 +); + +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- + +const __p_alias_pref = _simple_boolean_value_flag_prefernce_mapper( + keys.alias.as_p +); + +const __textspan_alias_pref = _simple_boolean_value_flag_prefernce_mapper( + keys.alias.as_span +); + +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- +// ----------------------------------------------------------------------------- + +const _simple_custom_string_value_flag_prefernce_mapper = ( + k: string | Array +): Option => + Array.isArray(k) + ? { + names: k, + // e.g. 100, 100px, 100%, . + type: "string", + } + : { + name: k, + // e.g. 100, 100px, 100%, . + type: "string", + }; + +const __width_alias_pref = _simple_custom_string_value_flag_prefernce_mapper( + keys.alias.width +); +const __max_width_alias_pref = + _simple_custom_string_value_flag_prefernce_mapper(keys.alias.max_width); +const __min_width_alias_pref = + _simple_custom_string_value_flag_prefernce_mapper(keys.alias.min_width); + +const __height_alias_pref = _simple_custom_string_value_flag_prefernce_mapper( + keys.alias.height +); +const __max_height_alias_pref = + _simple_custom_string_value_flag_prefernce_mapper(keys.alias.max_height); +const __min_height_alias_pref = + _simple_custom_string_value_flag_prefernce_mapper(keys.alias.min_height); + +function transform_wh_declaration_alias_from_raw(raw: { [key: string]: any }): { + [key: string]: WHDeclarationFlag; +} { + const handle = (key) => { + if (raw[key]) { + return { + [key]: { + flag: key, + value: Number(raw[key]), // TODO: add more parser + _raw: raw[key] as string, + } as WHDeclarationFlag, + }; + } + }; + + return [ + keys.flag_key__width, + keys.flag_key__min_width, + keys.flag_key__max_width, + + keys.flag_key__height, + keys.flag_key__min_height, + keys.flag_key__max_height, + ].reduce((acc, c) => { + const d = handle(c); + if (d) { + return { + ...acc, + ...d, + }; + } + return acc; + }, {}); +} + +const __fix_width_alias_pref = _simple_boolean_value_flag_prefernce_mapper( + keys.alias.fix_width +); +const __fix_height_alias_pref = _simple_boolean_value_flag_prefernce_mapper( + keys.alias.fix_height +); diff --git a/packages/support-flags/types.ts b/packages/support-flags/types.ts index fdfec241..8fc6ca12 100644 --- a/packages/support-flags/types.ts +++ b/packages/support-flags/types.ts @@ -1,5 +1,100 @@ -import { ArtworkFlag } from "./--artwork"; +import type { ArtworkFlag } from "./--artwork"; +import type { AsHeading1Flag } from "./--as-h1"; +import type { AsHeading2Flag } from "./--as-h2"; +import type { AsHeading3Flag } from "./--as-h3"; +import type { AsHeading4Flag } from "./--as-h4"; +import type { AsHeading5Flag } from "./--as-h5"; +import type { AsHeading6Flag } from "./--as-h6"; +import type { AsParagraphFlag } from "./--as-p"; +import type { AsTextSpanFlag } from "./--as-span"; +import type { WidthFlag } from "./--width"; +import type { MinWidthFlag } from "./--min-width"; +import type { MaxWidthFlag } from "./--max-width"; +import type { HeightFlag } from "./--height"; +import type { MinHeightFlag } from "./--min-height"; +import type { MaxHeightFlag } from "./--max-height"; +import { FixWidthFlag } from "./--fix-width"; +import { FixHeightFlag } from "./--fix-height"; -// export { ArtworkFlag }; +export type Flag = + // + ArtworkFlag | TextElementPreferenceFlag | WHDeclarationFlag | FixWHFlag; -export type Flag = ArtworkFlag; +export interface SimpleBooleanValueFlag { + flag: string; + value: boolean; + _raw?: string; +} + +export type WHDeclarationFlag = WidthDeclarationFlag | HeightDeclarationFlag; + +export type WidthDeclarationFlag = WidthFlag | MinWidthFlag | MaxWidthFlag; +export type HeightDeclarationFlag = HeightFlag | MinHeightFlag | MaxHeightFlag; + +export type FixWHFlag = FixWidthFlag | FixHeightFlag; + +/** + * Type alias for a flag that can be used to set the element preference to render a text + * + * - `h1` + * - `h2` + * - `h3` + * - `h4` + * - `h5` + * - `h6` + * - `p` + * - `span` + */ +export type TextElementPreferenceFlag = + | HeadingFlag + | AsParagraphFlag + | AsTextSpanFlag; + +/** + * Type alias for a flag that can be used to set the heading level. + * from 1 to 6. + * + * - `--as-h1` + * - `--as-h2` + * - `--as-h3` + * - `--as-h4` + * - `--as-h5` + * - `--as-h6` + */ +export type HeadingFlag = + | AsHeading1Flag + | AsHeading2Flag + | AsHeading3Flag + | AsHeading4Flag + | AsHeading5Flag + | AsHeading6Flag; + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- + +export type { ArtworkFlag }; + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- + +export type { + AsHeading1Flag, + AsHeading2Flag, + AsHeading3Flag, + AsHeading4Flag, + AsHeading5Flag, + AsHeading6Flag, +}; + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- + +export type { AsParagraphFlag }; +export type { AsTextSpanFlag }; + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- + +export type { WidthFlag, MinWidthFlag, MaxWidthFlag }; +export type { HeightFlag, MinHeightFlag, MaxHeightFlag }; +export type { FixWidthFlag, FixHeightFlag }; diff --git a/packages/support-flags/v/README.md b/packages/support-flags/v/README.md new file mode 100644 index 00000000..d495213b --- /dev/null +++ b/packages/support-flags/v/README.md @@ -0,0 +1,41 @@ +# Reserved Values + +## Falsy Values + +- true +- False +- yes + +## Truthy Values + +- True +- false +- no + +## Units + +- vw +- vh +- em +- px + +## Length + +## Keywords + +- this + +## Explicit String + +- "string value" + +**Example** + +> When specific key only excepts a string value, it will be treated as a string value. Otherwise, by default, It will be ignored. (It may also break whole process. be careful with it) + +``` +--name=0 // invalid 🚫 +--name="0" // valid 👍 +--name=true // invalid 🚫 +--name=true // valid 👍 +``` diff --git a/scripts/docs-copy/flags-api-docs.js b/scripts/docs-copy/flags-api-docs.js new file mode 100644 index 00000000..463bd6bb --- /dev/null +++ b/scripts/docs-copy/flags-api-docs.js @@ -0,0 +1,64 @@ +const fs = require("fs"); +const path = require("path"); + +const origin = "./packages/support-flags"; +const target = "./docs/flags"; +const github_base_url = + "https://github.com/gridaco/designto-code/tree/main/packages/support-flags"; +/** + * copies the README.md file under each directory under "origin" to "target" + * the README.md content will be preserved, but the file name will be changed to current folder name + * for example, if the file is `--action/README.md`, the file will be copied and renamed to `--action.md` + * + * the origin directory will have multiple subdirectory with one README.md file. + * these files under each subdirectory will be copied directly under "target" with above new file name. + */ +function copy() { + const files = []; + + // read all directories under origin directory with pattern that matches regex `/^--[a-z]+(.*?)$/` as prefix + // add README.md files to files array + fs.readdirSync(origin).forEach((dir) => { + if (dir.match(/^--[a-z]+(.*?)$/)) { + const dirPath = path.join(origin, dir); + const filesInDir = fs.readdirSync(dirPath); + filesInDir.forEach((file) => { + if (file === "README.md") { + files.push(path.join(dirPath, file)); + } + }); + } + }); + + // these are custom directories under origin. [`----disable`, `---custom`] + // add README.md under these directory, add to files array + ["----disable", "---custom"].forEach((dir) => { + const dirPath = path.join(origin, dir); + const filesInDir = fs.readdirSync(dirPath); + filesInDir.forEach((file) => { + if (file === "README.md") { + files.push(path.join(dirPath, file)); + } + }); + }); + + // read the content and extension of each file, save it to a map. where key being the file path, value being the content and extension + const fileMap = new Map(); + files.forEach((file) => { + const content = fs.readFileSync(file, "utf8"); + const ext = path.extname(file); + // make a key. the key is extracted from var - file, which is a paht. extract last directory name and use it as key. e.g. "../../--action/README.md" -> "--action" + const key = path.basename(path.dirname(file)); + fileMap.set(key, { content, ext }); + }); + + // copy each file to target directory + fileMap.forEach((value, key) => { + const newFile = path.join(target, `${key}${value.ext}`); + fs.writeFileSync(newFile, value.content); + }); +} + +if (require.main === module) { + copy(); +} diff --git a/scripts/docs-deploy/pre-deploy.sh b/scripts/docs-deploy/pre-deploy.sh new file mode 100644 index 00000000..6de5a01a --- /dev/null +++ b/scripts/docs-deploy/pre-deploy.sh @@ -0,0 +1 @@ +node ../docs-copy/flags-api-docs.js \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 0c0c6405..5318a80e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8190,6 +8190,11 @@ hosted-git-info@^2.1.4, hosted-git-info@^2.4.2: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== +hotkeys-js@3.8.7: + version "3.8.7" + resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.8.7.tgz#c16cab978b53d7242f860ca3932e976b92399981" + integrity sha512-ckAx3EkUr5XjDwjEHDorHxRO2Kb7z6Z2Sxul4MbBkN8Nho7XDslQsgMJT+CiJ5Z4TgRxxvKHEpuLE3imzqy4Lg== + html-encoding-sniffer@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" @@ -11800,6 +11805,13 @@ react-draggable@^4.0.3, react-draggable@^4.4.4: clsx "^1.1.1" prop-types "^15.6.0" +react-hotkeys-hook@^3.4.4: + version "3.4.4" + resolved "https://registry.yarnpkg.com/react-hotkeys-hook/-/react-hotkeys-hook-3.4.4.tgz#52ba5d8ef5e47cc2e776c70a9036d518e0993d51" + integrity sha512-vaORq07rWgmuF3owWRhgFV/3VL8/l2q9lz0WyVEddJnWTtKW+AOgU5YgYKuwN6h6h7bCcLG3MFsJIjCrM/5DvQ== + dependencies: + hotkeys-js "3.8.7" + react-is@16.13.1, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"