├── packages ├── console-ui │ ├── select │ │ └── index.ts │ ├── toast │ │ └── index.ts │ ├── label-chip │ │ └── index.ts │ ├── checkbox │ │ └── index.tsx │ ├── README.md │ ├── package.json │ ├── index.ts │ ├── divider │ │ └── index.tsx │ ├── form-field │ │ └── index.ts │ ├── LICENSE │ ├── text-form-field │ │ └── index.tsx │ ├── text-field │ │ └── index.tsx │ └── button │ │ └── index.tsx ├── editor-ui-breadcrumb │ ├── index.ts │ └── package.json ├── editor-ui-search │ ├── index.ts │ └── package.json ├── editor-ui │ ├── lib │ │ ├── layouts │ │ │ └── README.md │ │ ├── index.ts │ │ ├── other │ │ │ └── button │ │ │ │ ├── index.tsx │ │ │ │ └── button.stories.tsx │ │ ├── dashboard │ │ │ ├── guide │ │ │ │ ├── index.tsx │ │ │ │ └── guide.stories.tsx │ │ │ ├── dashboard.tsx │ │ │ ├── dashboard-cell │ │ │ │ ├── index.tsx │ │ │ │ └── dashbaord-cell.stories.tsx │ │ │ └── dashboard.stories.tsx │ │ ├── header │ │ │ ├── menu │ │ │ │ ├── index.tsx │ │ │ │ └── menu.stories.tsx │ │ │ ├── search-button │ │ │ │ ├── index.tsx │ │ │ │ └── search-button.stories.tsx │ │ │ └── navigation-button │ │ │ │ ├── index.tsx │ │ │ │ └── navigation-button.stories.tsx │ │ └── combined │ │ │ └── property-layer │ │ │ ├── only-text-field │ │ │ ├── only-text-field.stories.mdx │ │ │ └── index.tsx │ │ │ ├── only-two-text-field │ │ │ ├── only-text-field.stories.mdx │ │ │ └── index.tsx │ │ │ ├── text-field-n-toggle │ │ │ ├── text-field-n-toggle.stories.mdx │ │ │ └── index.tsx │ │ │ ├── only-toggle │ │ │ ├── index.tsx │ │ │ └── only-toggle.stories.mdx │ │ │ ├── text-filed-n-selector │ │ │ ├── text-filed-n-selector.stories.mdx │ │ │ └── index.tsx │ │ │ └── text-field-n-silder │ │ │ ├── text-field-n-silder.stories.mdx │ │ │ └── index.tsx │ ├── index.ts │ └── package.json ├── README.md ├── editor-ui-button │ ├── scaffolds │ │ ├── text-button.tsx │ │ ├── square-icon-button.tsx │ │ ├── index.ts │ │ ├── loading-button.tsx │ │ └── default-button.tsx │ ├── index.ts │ ├── loading-indicator │ │ ├── helpers │ │ │ ├── index.ts │ │ │ ├── colors.ts │ │ │ ├── prop-types.ts │ │ │ └── unit-converter.ts │ │ ├── interfaces.ts │ │ └── default-loading-indicator.tsx │ └── package.json ├── editor-ui-property │ ├── layout │ │ ├── property-list.tsx │ │ ├── property-input-cell.tsx │ │ ├── index.ts │ │ ├── dnd-item │ │ │ └── index.tsx │ │ └── dnd-wrapper │ │ │ └── index.tsx │ ├── readme.md │ ├── tsconfig.json │ ├── property-cell │ │ ├── index.ts │ │ ├── property-cell.stories.tsx │ │ └── property-cell.tsx │ ├── property-line │ │ ├── index.ts │ │ └── property-line.tsx │ ├── property-group │ │ ├── index.ts │ │ ├── property-group.tsx │ │ └── property-group.stories.tsx │ ├── property-lines │ │ ├── index.ts │ │ ├── property-lines.tsx │ │ └── property-lines.stories.tsx │ ├── property-input-toggle-group │ │ ├── index.ts │ │ └── property-input-toggle-group.tsx │ ├── property-input-color │ │ ├── index.ts │ │ └── property-input-color.tsx │ ├── property-input-json │ │ ├── index.ts │ │ ├── readme.md │ │ └── property-input-json.tsx │ ├── property-group-header │ │ ├── index.ts │ │ ├── property-group-header.stories.tsx │ │ └── property-group-header.tsx │ ├── property-input-select │ │ ├── index.ts │ │ └── property-input-select.tsx │ ├── property-input-slider │ │ ├── index.ts │ │ └── property-input-slider.tsx │ ├── property-input-checkbox │ │ ├── index.ts │ │ └── property-input-checkbox.tsx │ ├── theme-one │ │ ├── index.ts │ │ ├── light.ts │ │ └── dark.ts │ ├── property-input │ │ ├── index.ts │ │ ├── property-input.stories.tsx │ │ └── property-input.tsx │ ├── theme │ │ ├── index.ts │ │ ├── use-theme.ts │ │ ├── styled.ts │ │ ├── editor-ui-property-theme-provider.tsx │ │ └── theme.ts │ ├── .storybook │ │ ├── main.js │ │ └── preview.js │ ├── css │ │ └── index.ts │ ├── index.ts │ └── package.json ├── editor-ui-hierarchy │ ├── hierarchy-layer-item │ │ ├── index.ts │ │ └── props.ts │ ├── hierarchy-page-item │ │ ├── index.ts │ │ └── props.ts │ ├── hierarchy-segment │ │ ├── index.ts │ │ └── hierarchy-segment.tsx │ ├── hierarchy-segment-label │ │ ├── index.ts │ │ ├── hierarchy-segment-label.tsx │ │ └── hierarchy-segment-label-scaffold.tsx │ ├── index.ts │ ├── package.json │ ├── hierachy.stories.mdx │ ├── hierachy.tsx │ ├── item │ │ ├── item.stories.mdx │ │ └── index.tsx │ └── tree-view.tsx ├── editor-ui-multiplayer │ ├── multiplayer-cursor │ │ └── index.ts │ ├── multiplayer-avatar │ │ ├── index.ts │ │ └── multiplayer-avatar.tsx │ ├── multiplayer-avatar-group │ │ ├── index.ts │ │ └── multiplayer-avatar-group.tsx │ ├── index.ts │ └── package.json ├── editor-ui-panel │ ├── README.md │ └── package.json ├── editor-ui-tooltip │ ├── index.ts │ ├── tooltip │ │ ├── index.ts │ │ └── tooltip.tsx │ └── package.json ├── editor-ui-menu │ ├── index.ts │ ├── _internal │ │ ├── index.ts │ │ └── menu.tsx │ ├── README.md │ └── package.json ├── editor-ui-sortable │ ├── index.ts │ ├── README.md │ ├── package.json │ ├── LICENSE │ └── sortable.tsx ├── editor-ui-workspace │ ├── index.ts │ ├── package.json │ └── layouts │ │ ├── index.ts │ │ ├── workspace-layout │ │ └── index.tsx │ │ └── home-scaffold │ │ └── index.tsx ├── editor-ui-shortcut │ ├── README.md │ ├── package.json │ └── CONTRIBUTING.md ├── editor-ui-alert-dialog │ ├── index.ts │ ├── package.json │ └── alert-dialog.tsx ├── editor-ui-color-picker │ ├── index.ts │ ├── color-picker.tsx │ └── package.json ├── editor-ui-context-menu │ ├── index.tsx │ ├── context-menu │ │ ├── index.ts │ │ ├── context-menu-separator.tsx │ │ ├── context-menu-root.tsx │ │ └── context-menu-item.tsx │ └── package.json ├── editor-ui-dropdown-menu │ ├── index.ts │ ├── package.json │ └── dropdown-menu.tsx ├── editor-ui-desktop-titlebar │ ├── index.ts │ ├── README.md │ ├── package.json │ └── bar-drag-area.tsx ├── editor-ui-scroll-area │ ├── index.ts │ ├── README.md │ ├── package.json │ ├── LICENSE │ └── scroll-area.tsx ├── editor-ui-hooks │ ├── index.ts │ ├── package.json │ ├── use-window-size.ts │ └── use-hover.ts ├── editor-ui-spacer │ ├── index.ts │ ├── package.json │ └── spacer.ts ├── editor-ui-listview │ ├── index.ts │ ├── README.md │ ├── package.json │ └── LICENSE ├── editor-ui-toolbar │ └── package.json ├── editor-ui-utils │ ├── index.ts │ ├── package.json │ ├── with-separator.ts │ └── merge-event-handlers.ts ├── editor-ui-dialog │ ├── index.ts │ ├── dialog-control.ts │ ├── package.json │ ├── README.md │ ├── dialog-context-provider.tsx │ └── dialog.tsx ├── editor-ui-foundation │ └── package.json ├── editor-ui-icons │ ├── package.json │ ├── icons.stories.mdx │ ├── index.tsx │ └── icons.tsx └── editor-ui-theme │ ├── media-query.ts │ ├── package.json │ ├── index.ts │ ├── dark.ts │ ├── theme-provider.tsx │ └── light.ts ├── branding └── cover.png ├── .prettierrc ├── examples └── README.md ├── .storybook ├── preview.js └── main.js ├── tsconfig.json ├── CONTRIBUTING.md ├── .github └── workflows │ └── chromatic.yml ├── LICENSE ├── package.json ├── README.md └── .gitignore /packages/console-ui/select/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/console-ui/toast/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/editor-ui-breadcrumb/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/editor-ui-search/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/console-ui/label-chip/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/editor-ui/lib/layouts/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/README.md: -------------------------------------------------------------------------------- 1 | # `@editor-ui` pacakges 2 | -------------------------------------------------------------------------------- /packages/editor-ui-button/scaffolds/text-button.tsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/editor-ui-property/layout/property-list.tsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/editor-ui-button/scaffolds/square-icon-button.tsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/editor-ui-hierarchy/hierarchy-layer-item/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/editor-ui-hierarchy/hierarchy-page-item/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/editor-ui-multiplayer/multiplayer-cursor/index.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/editor-ui-property/layout/property-input-cell.tsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/editor-ui/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./lib"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-button/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./scaffolds" -------------------------------------------------------------------------------- /packages/editor-ui-panel/README.md: -------------------------------------------------------------------------------- 1 | # `@editor-ui/panel` 2 | -------------------------------------------------------------------------------- /packages/editor-ui-tooltip/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./tooltip" -------------------------------------------------------------------------------- /packages/editor-ui-menu/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./_internal"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-property/readme.md: -------------------------------------------------------------------------------- 1 | # `@editor-ui/property` 2 | -------------------------------------------------------------------------------- /packages/editor-ui-sortable/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./sortable"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-tooltip/tooltip/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./tooltip" -------------------------------------------------------------------------------- /packages/editor-ui-workspace/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./layouts"; 2 | -------------------------------------------------------------------------------- /packages/console-ui/checkbox/index.tsx: -------------------------------------------------------------------------------- 1 | export function Checkbox() {} 2 | -------------------------------------------------------------------------------- /packages/editor-ui-menu/_internal/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./menu"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-shortcut/README.md: -------------------------------------------------------------------------------- 1 | # Keymaps & Shortcut utility 2 | -------------------------------------------------------------------------------- /packages/editor-ui-alert-dialog/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./alert-dialog"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-color-picker/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./color-picker"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-context-menu/index.tsx: -------------------------------------------------------------------------------- 1 | export * from "./context-menu"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-dropdown-menu/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./dropdown-menu"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-desktop-titlebar/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./bar-drag-area"; 2 | -------------------------------------------------------------------------------- /branding/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gridaco/editor-ui/HEAD/branding/cover.png -------------------------------------------------------------------------------- /packages/editor-ui-property/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": false, 3 | "trailingComma": "es5", 4 | "semi": true 5 | } -------------------------------------------------------------------------------- /packages/editor-ui-hierarchy/hierarchy-layer-item/props.ts: -------------------------------------------------------------------------------- 1 | export interface LayerItemProps {} 2 | -------------------------------------------------------------------------------- /packages/editor-ui-hierarchy/hierarchy-segment/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./hierarchy-segment"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-scroll-area/index.ts: -------------------------------------------------------------------------------- 1 | export { default as default } from "./scroll-area"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-desktop-titlebar/README.md: -------------------------------------------------------------------------------- 1 | # Titlebar for frameless electron application (macOS) 2 | -------------------------------------------------------------------------------- /packages/editor-ui-multiplayer/multiplayer-avatar/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./multiplayer-avatar"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-cell/index.ts: -------------------------------------------------------------------------------- 1 | export { PropertyCell } from "./property-cell"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-line/index.ts: -------------------------------------------------------------------------------- 1 | export { PropertyLine } from "./property-line"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./use-hover"; 2 | export * from "./use-window-size"; 3 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-group/index.ts: -------------------------------------------------------------------------------- 1 | export { PropertyGroup } from "./property-group"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-lines/index.ts: -------------------------------------------------------------------------------- 1 | export { PropertyLines } from "./property-lines"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-spacer/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./spacer"; 2 | export * as Spacer from "./spacer"; 3 | -------------------------------------------------------------------------------- /packages/editor-ui-listview/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./listview"; 2 | export * as ListView from "./listview"; 3 | -------------------------------------------------------------------------------- /packages/editor-ui-multiplayer/multiplayer-avatar-group/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./multiplayer-avatar-group"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-search/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@editor-ui/search", 3 | "version": "0.0.0" 4 | } -------------------------------------------------------------------------------- /packages/editor-ui-shortcut/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@editor-ui/shortcut", 3 | "version": "0.0.0" 4 | } -------------------------------------------------------------------------------- /packages/editor-ui-toolbar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@editor-ui/toolbar", 3 | "version": "0.0.0" 4 | } -------------------------------------------------------------------------------- /packages/editor-ui-breadcrumb/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@editor-ui/breadcrumb", 3 | "version": "0.0.0" 4 | } -------------------------------------------------------------------------------- /packages/editor-ui-property/property-input-toggle-group/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./property-input-toggle-group"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./with-separator"; 2 | export * from "./merge-event-handlers"; 3 | -------------------------------------------------------------------------------- /packages/editor-ui-workspace/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@editor-ui/workspace", 3 | "version": "0.0.0" 4 | } -------------------------------------------------------------------------------- /packages/editor-ui-button/scaffolds/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./default-button"; 2 | export * from "./loading-button"; 3 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-input-color/index.ts: -------------------------------------------------------------------------------- 1 | export { PropertyColorInput } from "./property-input-color"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-input-json/index.ts: -------------------------------------------------------------------------------- 1 | export { PropertyJsonInput } from "./property-input-json"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-workspace/layouts/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./home-scaffold"; 2 | export * from "./workspace-layout"; 3 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-group-header/index.ts: -------------------------------------------------------------------------------- 1 | export { PropertyGroupHeader } from "./property-group-header"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-input-select/index.ts: -------------------------------------------------------------------------------- 1 | export { PropertySelectInput } from "./property-input-select"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-input-slider/index.ts: -------------------------------------------------------------------------------- 1 | export { PropertySliderInput } from "./property-input-slider"; 2 | -------------------------------------------------------------------------------- /packages/editor-ui-multiplayer/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./multiplayer-avatar"; 2 | export * from "./multiplayer-avatar-group"; 3 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-input-checkbox/index.ts: -------------------------------------------------------------------------------- 1 | export { PropertyCheckboxInput } from "./property-input-checkbox"; 2 | -------------------------------------------------------------------------------- /packages/console-ui/README.md: -------------------------------------------------------------------------------- 1 | # @editor-ui/console 2 | Reflect UI Package for Console web applications Powering General Grida UIs. 3 | -------------------------------------------------------------------------------- /packages/editor-ui-property/theme-one/index.ts: -------------------------------------------------------------------------------- 1 | export { default as light } from "./light"; 2 | export { default as dark } from "./dark"; 3 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-input-json/readme.md: -------------------------------------------------------------------------------- 1 | ## References 2 | 3 | - react-json-editor-ajrm 4 | - react-json-view 5 | - react-json-tree 6 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-input/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./property-input"; 2 | export type { PropertyInputProps } from "./property-input"; 3 | -------------------------------------------------------------------------------- /packages/editor-ui-button/loading-indicator/helpers/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./prop-types"; 2 | export * from "./colors"; 3 | export * from "./unit-converter"; 4 | -------------------------------------------------------------------------------- /packages/editor-ui-hierarchy/hierarchy-segment-label/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./hierarchy-segment-label"; 2 | export * from "./hierarchy-segment-label-scaffold"; 3 | -------------------------------------------------------------------------------- /packages/editor-ui-hierarchy/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./tree-view"; 2 | export * as TreeView from "./tree-view"; 3 | export * from "./hierachy"; 4 | // export * from "./item"; 5 | -------------------------------------------------------------------------------- /packages/editor-ui-context-menu/context-menu/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./context-menu-item"; 2 | export * from "./context-menu-root"; 3 | export * from "./context-menu-separator"; 4 | -------------------------------------------------------------------------------- /packages/editor-ui-panel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@editor-ui/panel", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "@editor-ui/foundation": "workspace:^" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/editor-ui-dialog/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./dialog"; 2 | export { ModalContextProvider } from "./dialog-context-provider"; 3 | export { showDialog, create } from "./dialog-control"; 4 | -------------------------------------------------------------------------------- /packages/editor-ui-foundation/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@editor-ui/foundation", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "@editor-ui/theme": "workspace:*" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/editor-ui-property/layout/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./dnd-item"; 2 | export * from "./dnd-wrapper"; 3 | // export * from "./property-input-cell"; 4 | // export * from "./property-list"; 5 | -------------------------------------------------------------------------------- /packages/editor-ui-shortcut/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Technical References 2 | 3 | > This are the official projects we reference to build this package. 4 | 5 | - react-hotkeys-hook 6 | - monaco-editor 7 | -------------------------------------------------------------------------------- /packages/editor-ui-desktop-titlebar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@editor-ui/desktop-titlebar", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "@editor-ui/foundation": "workspace:^" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/editor-ui-multiplayer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@editor-ui/multiplayer", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "@radix-ui/react-avatar": "^0.0.14" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-input-json/property-input-json.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | /** 4 | * @deprecated not ready yet 5 | */ 6 | export function PropertyJsonInput() { 7 | return <>; 8 | } 9 | -------------------------------------------------------------------------------- /packages/console-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@editor-ui/console", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "@radix-ui/react-toast": "^1.0.0", 6 | "install": "^0.13.0", 7 | "npm": "^8.18.0" 8 | } 9 | } -------------------------------------------------------------------------------- /packages/editor-ui-property/theme/index.ts: -------------------------------------------------------------------------------- 1 | export { EditorPropertyThemeProvider } from "./editor-ui-property-theme-provider"; 2 | export type { EditorPropertyTheme } from "./theme"; 3 | export { useTheme } from "./use-theme"; 4 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | ## simple 4 | 5 | ## usage in production 6 | 7 | - [bridged](https://github.com/bridgedxyz/bridged) 8 | 9 | ## realwork copy example 10 | 11 | - vscode 12 | - notion 13 | - sketch 14 | -------------------------------------------------------------------------------- /packages/editor-ui-property/theme/use-theme.ts: -------------------------------------------------------------------------------- 1 | import { useTheme as _useTheme } from "@emotion/react"; 2 | import { EditorPropertyTheme } from "./theme"; 3 | 4 | export const useTheme: () => EditorPropertyTheme = _useTheme as any; 5 | -------------------------------------------------------------------------------- /packages/editor-ui/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from "@editor-ui/hierarchy"; 2 | export * as hooks from "@editor-ui/hooks"; 3 | export * from "@editor-ui/button"; 4 | export * from "@editor-ui/spacer"; 5 | export * from "@editor-ui/utils"; 6 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | 2 | export const parameters = { 3 | actions: { argTypesRegex: "^on[A-Z].*" }, 4 | controls: { 5 | matchers: { 6 | color: /(background|color)$/i, 7 | date: /Date$/, 8 | }, 9 | }, 10 | } -------------------------------------------------------------------------------- /packages/editor-ui-dialog/dialog-control.ts: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { create as _create } from "react-modal-promise"; 3 | // 4 | export function showDialog(d) { 5 | _create(d)(); 6 | } 7 | export const create = _create; 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strict": false, 4 | "module": "esnext", 5 | "target": "ES2017", 6 | "jsx": "react", 7 | "moduleResolution": "node", 8 | "esModuleInterop": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/editor-ui-sortable/README.md: -------------------------------------------------------------------------------- 1 | # Sortable 2 | 3 | ## Disclaimer 4 | 5 | > This package was originally copied from [here](https://github.com/noya-app/noya/blob/main/packages/noya-designsystem/src/components/Sortable.tsx) under MIT License @2021.9 6 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Reflect Editor UI 2 | 3 | ## Read reflect-ui contribution guideline first 4 | 5 | WIP 6 | 7 | ## General React Coding conventions 8 | 9 | https://github.com/bridgedxyz/.github/blob/main/contributing/react.md 10 | -------------------------------------------------------------------------------- /packages/editor-ui-dropdown-menu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@editor-ui/dropdown-menu", 3 | "version": "0.0.0", 4 | "private": false, 5 | "dependencies": { 6 | "@radix-ui/react-dropdown-menu": "^2.0.6", 7 | "@stitches/react": "^1.2.8" 8 | } 9 | } -------------------------------------------------------------------------------- /packages/editor-ui/lib/other/button/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from '@emotion/styled'; 3 | 4 | function Button() { 5 | return ( 6 |
7 | 8 |
9 | ) 10 | } 11 | 12 | export default Button 13 | -------------------------------------------------------------------------------- /packages/editor-ui-dialog/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@editor-ui/dialog", 3 | "version": "0.0.0", 4 | "main": "index.ts", 5 | "dependencies": { 6 | "@radix-ui/react-dialog": "^0.0.19", 7 | "react-modal-promise": "^0.7.6" 8 | } 9 | } -------------------------------------------------------------------------------- /packages/editor-ui-scroll-area/README.md: -------------------------------------------------------------------------------- 1 | # Scroll Area 2 | 3 | ## Disclaimer 4 | 5 | > This package was originally copied from [here](https://github.com/noya-app/noya/blob/main/packages/noya-designsystem/src/components/ScrollArea.tsx) under MIT License @2021.9 6 | -------------------------------------------------------------------------------- /packages/editor-ui-menu/README.md: -------------------------------------------------------------------------------- 1 | # `@editor-ui/menu` 2 | 3 | ## References 4 | 5 | - https://developer.apple.com/design/human-interface-guidelines/macos/menus/menu-anatomy/ 6 | - https://developer.apple.com/design/human-interface-guidelines/macos/menus/menu-bar-menus/ 7 | -------------------------------------------------------------------------------- /packages/editor-ui-property/theme/styled.ts: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | import { EditorPropertyTheme } from "./theme"; 3 | 4 | declare module "@emotion/react" { 5 | export interface Theme extends EditorPropertyTheme {} 6 | } 7 | 8 | export default styled; 9 | -------------------------------------------------------------------------------- /packages/editor-ui-color-picker/color-picker.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { RgbaColorPicker } from "react-colorful"; 3 | 4 | export function ColorPicker( 5 | props: React.ComponentProps 6 | ) { 7 | return ; 8 | } 9 | -------------------------------------------------------------------------------- /packages/editor-ui/lib/dashboard/guide/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from '@emotion/styled'; 3 | 4 | export interface DashboardGuideProps {} 5 | 6 | function DashboardGuide(props: DashboardGuideProps) { 7 | return
; 8 | } 9 | 10 | export default DashboardGuide; 11 | -------------------------------------------------------------------------------- /packages/editor-ui-hierarchy/hierarchy-page-item/props.ts: -------------------------------------------------------------------------------- 1 | export interface SinglePageItemProps { 2 | icon: string; 3 | name: string; 4 | expanded: boolean; 5 | depth: number; 6 | } 7 | 8 | export interface PageItemTreeProps extends SinglePageItemProps { 9 | children?: PageItemTreeProps[]; 10 | } 11 | -------------------------------------------------------------------------------- /packages/editor-ui-dialog/README.md: -------------------------------------------------------------------------------- 1 | # `@editor-ui/doalog` _(modal)_ 2 | 3 | ```sh 4 | yarn add @editor-ui/dialog 5 | ``` 6 | 7 | ## Usage 8 | 9 | ```tsx 10 | import { showModal } from "@editor-ui/dialog"; 11 | 12 | showModal(<>Hi).then((d) => { 13 | // do something with modal result 14 | }); 15 | ``` 16 | -------------------------------------------------------------------------------- /packages/console-ui/index.ts: -------------------------------------------------------------------------------- 1 | export { TextFormField } from "./text-form-field"; 2 | export { TextField } from "./text-field"; 3 | export { Button } from "./button"; 4 | export { Divider } from "./divider"; 5 | export { 6 | FormFieldBase, 7 | FormFieldLabel, 8 | FormFieldAssisiveText, 9 | } from "./form-field"; 10 | -------------------------------------------------------------------------------- /packages/editor-ui-property/.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stories: ["../**/*.stories.mdx", "../**/*.stories.@(js|jsx|ts|tsx)"], 3 | addons: [ 4 | "@storybook/addon-links", 5 | "@storybook/addon-essentials", 6 | "@storybook/addon-interactions", 7 | ], 8 | framework: "@storybook/react", 9 | }; 10 | -------------------------------------------------------------------------------- /packages/editor-ui/lib/dashboard/dashboard.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from '@emotion/styled'; 3 | export interface DashBoardProps { 4 | 5 | } 6 | 7 | function DashBoard(props: DashBoardProps) { 8 | return ( 9 |
10 | 11 |
12 | ) 13 | } 14 | 15 | export default DashBoard 16 | -------------------------------------------------------------------------------- /packages/editor-ui/lib/header/menu/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from '@emotion/styled'; 3 | 4 | export interface HeaderMenuProps { 5 | 6 | } 7 | 8 | function HeaderMenu(props : HeaderMenuProps) { 9 | return ( 10 |
11 | 12 |
13 | ) 14 | } 15 | 16 | export default HeaderMenu 17 | -------------------------------------------------------------------------------- /packages/editor-ui-spacer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@editor-ui/spacer", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "@editor-ui/foundation": "workspace:^" 6 | }, 7 | "devDependencies": { 8 | "@emotion/styled": "^11.11.0" 9 | }, 10 | "peerDependencies": { 11 | "@emotion/styled": "^11.11.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/editor-ui/lib/dashboard/dashboard-cell/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from '@emotion/styled'; 3 | 4 | export interface DashboardCellProps { 5 | 6 | } 7 | 8 | function DashboardCell(props : DashboardCellProps) { 9 | return ( 10 |
11 | 12 |
13 | ) 14 | } 15 | 16 | export default DashboardCell 17 | -------------------------------------------------------------------------------- /packages/editor-ui-dialog/dialog-context-provider.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ModalContainer from "react-modal-promise"; 3 | 4 | export function ModalContextProvider(props: { 5 | children: React.ReactNode | React.ReactNode[]; 6 | }) { 7 | return ( 8 | <> 9 | 10 | {props.children} 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /packages/editor-ui-menu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@editor-ui/menu", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "@editor-ui/foundation": "workspace:^", 6 | "@editor-ui/theme": "workspace:^" 7 | }, 8 | "devDependencies": { 9 | "@emotion/css": "^11.11.2" 10 | }, 11 | "peerDependencies": { 12 | "@emotion/css": "^11.11.2" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/editor-ui-listview/README.md: -------------------------------------------------------------------------------- 1 | # List view with dnd & sortable 2 | 3 | ## LICENSE - MIT 4 | 5 | This package was initially copy & pasted from noya design system. 6 | 7 | ## Disclaimer 8 | 9 | > This package was originally copied from [here](https://github.com/noya-app/noya/blob/main/packages/noya-designsystem/src/components/ListView.tsx) under MIT License @2021.9 10 | -------------------------------------------------------------------------------- /packages/editor-ui/lib/header/search-button/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from '@emotion/styled'; 3 | 4 | export interface HeaderSearchButtonProps { 5 | 6 | } 7 | 8 | function HeaderSearchButton(props : HeaderSearchButtonProps) { 9 | return ( 10 |
11 | 12 |
13 | ) 14 | } 15 | 16 | export default HeaderSearchButton 17 | -------------------------------------------------------------------------------- /packages/console-ui/divider/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | 4 | export function Divider({ 5 | width, 6 | }: { 7 | width?: React.CSSProperties["width"] | "full"; 8 | }) { 9 | return ; 10 | } 11 | 12 | const Line = styled.hr` 13 | border: none; 14 | border-top: solid 1px rgba(0, 0, 0, 0.15); 15 | width: 100%; 16 | `; 17 | -------------------------------------------------------------------------------- /packages/editor-ui-color-picker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@editor-ui/color-picker", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "react-colorful": "^5.6.1" 6 | }, 7 | "devDependencies": { 8 | "@types/react": "18.2.58", 9 | "react": "^18.2.0" 10 | }, 11 | "peerDependencies": { 12 | "@types/react": "18.2.58", 13 | "react": "^18.2.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/editor-ui-hierarchy/hierarchy-segment-label/hierarchy-segment-label.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | export function HierarchySegmentLabel(props: { 4 | name: string; 5 | expandToggle: JSX.Element; 6 | actions: JSX.Element[]; 7 | }) { 8 | return {props.name}; 9 | } 10 | 11 | const Wrapper = styled.div``; 12 | -------------------------------------------------------------------------------- /packages/editor-ui-context-menu/context-menu/context-menu-separator.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import { SEPARATOR_ITEM, styles } from "@editor-ui/menu"; 4 | import { Separator as _Separator } from "@radix-ui/react-context-menu"; 5 | 6 | export { SEPARATOR_ITEM }; 7 | 8 | export const ContextMenuSeparator = styled(_Separator)(styles.separatorStyle); 9 | -------------------------------------------------------------------------------- /packages/editor-ui-hooks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@editor-ui/hooks", 3 | "version": "0.0.0", 4 | "description": "React hooks fro editor-ui on for react", 5 | "devDependencies": { 6 | "@types/react": "18.2.58", 7 | "react": "^18.2.0" 8 | }, 9 | "peerDependencies": { 10 | "@types/react": "18.2.58", 11 | "react": "^18.2.0" 12 | } 13 | } -------------------------------------------------------------------------------- /packages/editor-ui/lib/header/navigation-button/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from '@emotion/styled'; 3 | 4 | export interface HeadernavigationButtonProps { 5 | 6 | } 7 | 8 | function HeaderNavigationButton(props : HeadernavigationButtonProps) { 9 | return ( 10 |
11 | 12 |
13 | ) 14 | } 15 | 16 | export default HeaderNavigationButton 17 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-group/property-group.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | import React from "react"; 3 | 4 | export function PropertyGroup({ children }: React.PropsWithChildren<{}>) { 5 | return {children}; 6 | } 7 | 8 | const PropertyGroupContainer = styled.div` 9 | display: flex; 10 | flex-direction: column; 11 | `; 12 | -------------------------------------------------------------------------------- /packages/editor-ui/lib/other/button/button.stories.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Story, Meta } from "@storybook/react"; 3 | 4 | import Button from "./"; 5 | 6 | export default { 7 | title: "Other/Button", 8 | component: Button, 9 | } as Meta; 10 | 11 | const Template: Story = (args) => 59 | ); 60 | } 61 | 62 | const Button = styled.button<{ 63 | widht: number; 64 | height: number; 65 | }>` 66 | width: ${(p) => (p.widht ? p.widht + "px" : undefined)}; 67 | height: ${(p) => (p.height ? p.height + "px" : undefined)}; 68 | min-height: 24px; 69 | height: 100%; 70 | border: none; 71 | background: black; 72 | color: white; 73 | padding: 16px 20px; 74 | border-radius: 4px; 75 | font-size: large; 76 | `; 77 | -------------------------------------------------------------------------------- /packages/editor-ui-hierarchy/item/item.stories.mdx: -------------------------------------------------------------------------------- 1 | import { Meta, Story, Canvas } from "@storybook/addon-docs/blocks"; 2 | 3 | import HirachyItem from "./"; 4 | 5 | 6 | 7 | # Hirachy Item 8 | 9 | `Hirachy Item` Component is for representing a **single structure** 10 | 11 | 12 | 13 |
14 | 21 |
22 |
23 |
24 | 25 | ## Select Item & UnSelect Item 26 | 27 | **Select Item** : Background color changes when users perform events such as click. 28 | 29 | **UnSelect Item** : Changes to Select Item when an unselected item, mouse-hover event, or click event occurs 30 | 31 | 32 | 33 |
34 |
35 | 43 |
44 | 51 |
52 |
53 |
54 | 55 | ## Item Level Variant (0 ~ 3) 56 | 57 | **Struct's Deps** are called **levels** 58 | 59 | Margin formula for level : 60 | 61 | ```js 62 | const levelMargin = 14; 63 | const defaultMargin = 25; 64 | 65 | return level * levelMargin + defaultMargin; 66 | ``` 67 | 68 | 69 | 70 |
71 | {Array(4) 72 | .fill(null) 73 | .map((_, ix) => ( 74 |
75 | 83 |
84 | ))} 85 |
86 |
87 |
88 | -------------------------------------------------------------------------------- /packages/editor-ui-hierarchy/tree-view.tsx: -------------------------------------------------------------------------------- 1 | import React, { 2 | //@ts-ignore 3 | ForwardedRef, 4 | forwardRef, 5 | memo, 6 | ReactNode, 7 | useCallback, 8 | useContext, 9 | } from "react"; 10 | import { ChevronDownIcon, ChevronRightIcon } from "@radix-ui/react-icons"; 11 | import styled from "@emotion/styled"; 12 | import { ListView } from "@editor-ui/listview"; 13 | import { ListViewRowProps } from "@editor-ui/listview"; 14 | import * as Spacer from "@editor-ui/spacer"; 15 | 16 | /* ---------------------------------------------------------------------------- 17 | * Row 18 | * ------------------------------------------------------------------------- */ 19 | 20 | type TreeRowBaseProps = { 21 | icon?: ReactNode; 22 | depth: number; 23 | expanded?: boolean; 24 | onClickChevron?: () => void; 25 | }; 26 | 27 | const ChevronContainer = styled.span({ 28 | display: "flex", 29 | alignItems: "center", 30 | }); 31 | 32 | export type TreeRowProps< 33 | MenuItemType extends string 34 | > = ListViewRowProps & TreeRowBaseProps; 35 | 36 | const TreeRow = forwardRef(function TreeRow( 37 | { 38 | depth, 39 | icon, 40 | expanded, 41 | onClickChevron, 42 | children, 43 | ...rest 44 | }: TreeRowProps, 45 | forwardedRef: ForwardedRef 46 | ) { 47 | const { expandable } = useContext(ListView.ListRowContext); 48 | 49 | const handleClickChevron = useCallback( 50 | (event) => { 51 | event.stopPropagation(); 52 | onClickChevron?.(); 53 | }, 54 | [onClickChevron] 55 | ); 56 | 57 | return ( 58 | 59 | 60 | {expandable && ( 61 | <> 62 | {expanded === undefined ? ( 63 | 64 | ) : ( 65 | 66 | {expanded ? : } 67 | 68 | )} 69 | 70 | 71 | )} 72 | {icon && ( 73 | <> 74 | {icon} 75 | 76 | 77 | )} 78 | {children} 79 | 80 | ); 81 | }); 82 | 83 | /* ---------------------------------------------------------------------------- 84 | * Root 85 | * ------------------------------------------------------------------------- */ 86 | 87 | export const Root = ListView.Root; 88 | export const RowTitle = ListView.RowTitle; 89 | export const Row = memo(TreeRow); 90 | export type TreeViewClickInfo = ListView.ListViewClickInfo; 91 | -------------------------------------------------------------------------------- /packages/editor-ui-alert-dialog/alert-dialog.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { styled, keyframes } from "@stitches/react"; 3 | import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"; 4 | import type { DialogContentProps } from "@radix-ui/react-dialog"; 5 | 6 | const overlayShow = keyframes({ 7 | "0%": { opacity: 0 }, 8 | "100%": { opacity: 1 }, 9 | }); 10 | 11 | const contentShow = keyframes({ 12 | "0%": { opacity: 0, transform: "translate(-50%, -48%) scale(.96)" }, 13 | "100%": { opacity: 1, transform: "translate(-50%, -50%) scale(1)" }, 14 | }); 15 | 16 | const StyledOverlay = styled(AlertDialogPrimitive.Overlay, { 17 | backgroundColor: "rgba(0, 0, 0, 0.1)", 18 | position: "fixed", 19 | zIndex: 9999, 20 | inset: 0, 21 | "@media (prefers-reduced-motion: no-preference)": { 22 | animation: `${overlayShow} 150ms cubic-bezier(0.16, 1, 0.3, 1) forwards`, 23 | }, 24 | }); 25 | 26 | const StyledContent = styled(AlertDialogPrimitive.Content, { 27 | backgroundColor: "white", 28 | zIndex: 99999, 29 | borderRadius: 6, 30 | boxShadow: 31 | "hsl(206 22% 7% / 35%) 0px 10px 38px -10px, hsl(206 22% 7% / 20%) 0px 10px 20px -15px", 32 | position: "fixed", 33 | top: "50%", 34 | left: "50%", 35 | transform: "translate(-50%, -50%)", 36 | width: "90vw", 37 | maxWidth: "500px", 38 | maxHeight: "85vh", 39 | padding: 25, 40 | "@media (prefers-reduced-motion: no-preference)": { 41 | animation: `${contentShow} 150ms cubic-bezier(0.16, 1, 0.3, 1) forwards`, 42 | }, 43 | "&:focus": { outline: "none" }, 44 | }); 45 | 46 | function Content({ 47 | children, 48 | ...props 49 | }: React.PropsWithChildren) { 50 | return ( 51 | 52 | 53 | {children} 54 | 55 | ); 56 | } 57 | 58 | const StyledTitle = styled(AlertDialogPrimitive.Title, { 59 | margin: 0, 60 | color: "rgba(0, 0, 0, 0.95)", 61 | fontSize: 17, 62 | fontWeight: 500, 63 | }); 64 | 65 | const StyledDescription = styled(AlertDialogPrimitive.Description, { 66 | marginBottom: 20, 67 | color: `rgba(0, 0, 0, 0.5)`, 68 | fontSize: 15, 69 | lineHeight: 1.5, 70 | }); 71 | 72 | // Exports 73 | export const AlertDialog = AlertDialogPrimitive.Root; 74 | export const AlertDialogTrigger = AlertDialogPrimitive.Trigger; 75 | export const AlertDialogContent = Content; 76 | export const AlertDialogTitle = StyledTitle; 77 | export const AlertDialogDescription = StyledDescription; 78 | export const AlertDialogAction = AlertDialogPrimitive.Action; 79 | export const AlertDialogCancel = AlertDialogPrimitive.Cancel; 80 | export const AlertDialogOverlay = StyledOverlay; 81 | export { AlertDialogPrimitive }; 82 | -------------------------------------------------------------------------------- /packages/editor-ui-button/scaffolds/default-button.tsx: -------------------------------------------------------------------------------- 1 | import React, { ForwardedRef, forwardRef, memo, ReactNode } from "react"; 2 | // import styled from "@emotion/styled"; 3 | import { Tooltip } from "@editor-ui/tooltip"; 4 | import styled from "@editor-ui/theme"; 5 | 6 | type ButtonVariant = "normal" | "thin"; 7 | 8 | /* ---------------------------------------------------------------------------- 9 | * Element 10 | * ------------------------------------------------------------------------- */ 11 | 12 | const ButtonElement = styled.button<{ 13 | active: boolean; 14 | disabled: boolean; 15 | variant: ButtonVariant; 16 | }>(({ theme, active, disabled, variant }) => ({ 17 | ...theme.textStyles.small, 18 | flex: "0 0 auto", 19 | position: "relative", 20 | border: "0", 21 | outline: "none", 22 | minWidth: variant === "thin" ? undefined : "31px", 23 | textAlign: "left", 24 | borderRadius: "4px", 25 | paddingTop: "4px", 26 | paddingRight: variant === "thin" ? "1px" : "6px", 27 | paddingBottom: variant === "thin" ? "0px" : "4px", 28 | paddingLeft: variant === "thin" ? "1px" : "6px", 29 | background: active ? theme.colors.primary : theme.colors.inputBackground, 30 | color: active ? "white" : theme.colors.text, 31 | opacity: disabled ? 0.25 : 1, 32 | "&:focus": { 33 | boxShadow: `0 0 0 1px ${theme.colors.sidebar.background}, 0 0 0 3px ${theme.colors.primary}`, 34 | }, 35 | display: "flex", 36 | alignItems: "center", 37 | justifyContent: "center", 38 | 39 | "& *": { 40 | pointerEvents: "none", 41 | }, 42 | })); 43 | 44 | /* ---------------------------------------------------------------------------- 45 | * Content 46 | * ------------------------------------------------------------------------- */ 47 | 48 | const ButtonContent = styled.span(({ theme }) => ({ 49 | // Line height of small text - maybe figure out better way to ensure 50 | // icons don't have a smaller height 51 | minHeight: "19px", 52 | display: "flex", 53 | alignItems: "center", 54 | })); 55 | 56 | /* ---------------------------------------------------------------------------- 57 | * Root 58 | * ------------------------------------------------------------------------- */ 59 | 60 | interface ButtonRootProps { 61 | id: string; 62 | children: ReactNode; 63 | active?: boolean; 64 | disabled?: boolean; 65 | variant?: ButtonVariant; 66 | tooltip?: ReactNode; 67 | onClick?: () => void; 68 | } 69 | 70 | export const Button = memo( 71 | forwardRef(function Button( 72 | { 73 | id, 74 | tooltip, 75 | active = false, 76 | disabled = false, 77 | variant = "normal", 78 | onClick, 79 | children, 80 | }: ButtonRootProps, 81 | forwardedRef: ForwardedRef 82 | ) { 83 | const buttonElement = ( 84 | 92 | {children} 93 | 94 | ); 95 | 96 | return tooltip ? ( 97 | {buttonElement} 98 | ) : ( 99 | buttonElement 100 | ); 101 | }) 102 | ); 103 | -------------------------------------------------------------------------------- /packages/editor-ui-hierarchy/item/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import { HierarchyData } from "../hierachy"; 4 | import Icon from "@editor-ui/icons"; 5 | 6 | interface HirachyItemStyledProps { 7 | ml?: number; 8 | selected?: boolean; 9 | expanded?: boolean; 10 | } 11 | 12 | export interface HirachyItemProps { 13 | /** */ 14 | onSelect?: () => void; 15 | /** If child exists, the function used to expand */ 16 | onExpand?: () => void; 17 | /** A single Struct for visualization */ 18 | data: HierarchyData; 19 | /** user selects an item or not item `click, mouse-hover` */ 20 | selected?: boolean; 21 | /** margin level of item `14 + level + defaultMargin` */ 22 | level: number; 23 | /** */ 24 | expanded?: boolean; 25 | /** */ 26 | children?: React.ReactNode; 27 | } 28 | 29 | function returnTypeIcon(type: HierarchyData["type"]) { 30 | switch (type) { 31 | case "icon": 32 | return ; 33 | case "layout": 34 | return ; 35 | case "image": 36 | return ; 37 | case "text": 38 | return ; 39 | } 40 | } 41 | 42 | export function HierachyItem(props: HirachyItemProps) { 43 | const { 44 | onExpand, 45 | onSelect, 46 | level, 47 | selected, 48 | expanded, 49 | data: { title, children: child, type }, 50 | children, 51 | ...rest 52 | } = props; 53 | return ( 54 | 55 |
56 | {expanded != null && ( 57 | 58 | )} 59 |
60 |
61 | {returnTypeIcon(type)} 62 | {title} 63 |
64 | {children} 65 |
66 | ); 67 | } 68 | 69 | HierachyItem.defaultProps = { 70 | isSelect: false, 71 | level: 0, 72 | }; 73 | 74 | const Wrapper = styled.div` 75 | position: relative; 76 | background-color: ${(p) => p.selected && `#514EFD`}; 77 | padding-left: ${(p) => p.ml}px; 78 | height: 30px; 79 | margin-right: 9px; 80 | margin: 3px 0px; 81 | border-radius: 6px; 82 | cursor: pointer; 83 | transition: all 0.2s ease; 84 | display: flex; 85 | align-items: center; 86 | justify-content: space-between; 87 | 88 | .indicator { 89 | display: flex; 90 | align-items: center; 91 | padding: 4px; 92 | } 93 | 94 | .label { 95 | display: flex; 96 | align-items: center; 97 | height: 100%; 98 | width: 80%; 99 | 100 | span { 101 | margin-left: 4px; 102 | color: ${(p) => (p.selected ? `#fff` : `#000`)}; 103 | font-size: 12px; 104 | } 105 | } 106 | 107 | &:hover { 108 | background-color: #514efd; 109 | 110 | span { 111 | color: #fff; 112 | } 113 | } 114 | `; 115 | 116 | const CustomIcon = styled(Icon)` 117 | transition: all 0.3s ease; 118 | ${(p) => 119 | p.expanded && 120 | ` 121 | transform : rotate(90deg); 122 | 123 | svg { 124 | path { 125 | fill: #000; 126 | } 127 | } 128 | `} 129 | `; 130 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-input-color/property-input-color.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "../theme/styled"; 3 | import { useTheme } from "../theme"; 4 | import { PropertyCell } from "../property-cell"; 5 | 6 | interface ColorInputProps { 7 | autofocus?: boolean; 8 | placeholder?: string; 9 | type?: "color"; 10 | value?: string | number; 11 | readonly?: boolean; 12 | disabled?: boolean; 13 | 14 | onChange?: (value: string) => void; 15 | onClick?: () => void; 16 | } 17 | 18 | export function PropertyColorInput({ 19 | autofocus = false, 20 | placeholder, 21 | value: initial = "", 22 | readonly, 23 | disabled, 24 | onChange, 25 | onClick, 26 | }: ColorInputProps) { 27 | const [value, setValue] = React.useState(initial); 28 | const [focused, setFocused] = React.useState(false); 29 | const theme = useTheme(); 30 | const inputRef = React.useRef(null); 31 | 32 | const onfocus = () => { 33 | setFocused(true); 34 | }; 35 | 36 | const onfocuscapture = () => { 37 | // select all text on focus 38 | inputRef.current?.select(); 39 | }; 40 | 41 | const onblur = () => { 42 | setFocused(false); 43 | }; 44 | 45 | const onvalue = (value: string) => { 46 | setValue(value); 47 | onChange?.(value); 48 | }; 49 | 50 | return ( 51 | 58 | onvalue(e.target.value)} 67 | style={{ 68 | margin: 0, 69 | }} 70 | /> 71 | onvalue(e.target.value)} 82 | placeholder={placeholder} 83 | style={{ 84 | margin: 0, 85 | }} 86 | /> 87 | 88 | ); 89 | } 90 | 91 | const ColorPickerInput = styled.input` 92 | height: 100%; 93 | border: none; 94 | outline: none; 95 | background: transparent; 96 | color: ${(props) => 97 | /* @ts-ignore */ 98 | props.theme.input.value}; 99 | font-size: 11px; 100 | font-weight: 500; 101 | padding: 0; 102 | `; 103 | 104 | const ColorTextInput = styled.input` 105 | width: 100%; 106 | height: 100%; 107 | border: none; 108 | outline: none; 109 | background: transparent; 110 | color: ${(props) => 111 | /* @ts-ignore */ 112 | props.theme.input.value}; 113 | font-size: 11px; 114 | font-weight: 500; 115 | padding: 0; 116 | 117 | &:disabled { 118 | color: ${(props) => 119 | /* @ts-ignore */ 120 | props.theme.input.value_disabled}; 121 | } 122 | 123 | ::placeholder { 124 | color: #999; 125 | } 126 | `; 127 | -------------------------------------------------------------------------------- /packages/editor-ui-theme/light.ts: -------------------------------------------------------------------------------- 1 | import { mediaQuery } from "./media-query"; 2 | import { CSSObject } from "@emotion/css"; 3 | 4 | export const colors = { 5 | text: "rgb(85, 85, 85)", 6 | textMuted: "rgb(166, 166, 166)", 7 | textDisabled: "rgb(166, 166, 166)", 8 | textDecorativeLight: "rgb(168, 185, 212)", 9 | textLink: "rgb(58, 108, 234)", 10 | textLinkFocused: "rgb(35, 82, 124)", 11 | divider: "rgba(0, 0, 0, 0.07)", 12 | dividerStrong: "rgba(0, 0, 0, 0.09)", 13 | primary: "#DADADA", 14 | primaryLight: "##F7F7F7", 15 | primaryDark: "#A6A6A6", 16 | neutralBackground: "rgb(222,223,232)", 17 | inputBackground: "rgb(240, 240, 242)", 18 | codeBackground: "rgb(250, 250, 250)", 19 | selectedBackground: "rgb(242, 245, 250)", 20 | activeBackground: "rgba(0,0,0,0.1)", 21 | dragOutline: "#A6A6A6", 22 | banner: { 23 | top: "rgb(222, 229, 255)", 24 | bottom: "rgba(252, 252, 254, 1)", 25 | }, 26 | title: { 27 | get left() { 28 | return colors.primary; 29 | }, 30 | right: "#b9a2ff", 31 | // right: '#93d8ff' 32 | }, 33 | button: { 34 | primaryText: "white", 35 | secondaryText: "white", 36 | get primaryBackground() { 37 | return colors.primary; 38 | }, 39 | secondaryBackground: "rgb(160, 160, 160)", 40 | }, 41 | listView: { 42 | raisedBackground: "rgba(255,255,255,0.8)", 43 | }, 44 | canvas: { 45 | background: "rgb(249,249,249)", 46 | dragHandleStroke: "rgba(180,180,180,0.5)", 47 | }, 48 | sidebar: { 49 | background: "rgba(252,252,252,0.85)", 50 | }, 51 | popover: { 52 | background: "rgb(252,252,252)", 53 | }, 54 | slider: { 55 | background: "white", 56 | border: "#BBB", 57 | }, 58 | icon: "rgb(139, 139, 139)", 59 | iconSelected: "rgb(220, 220, 220)", 60 | mask: "rgb(12,193,67)", 61 | }; 62 | 63 | export const fonts = { 64 | normal: "'Helvetica Neue', Helvetica, Arial, sans-serif", 65 | monospace: "Menlo, Monaco, Consolas, 'Courier New', monospace", 66 | }; 67 | 68 | // The last one, 0.85, I just eyeballed 69 | const typeScale = [3.052, 2.441, 1.953, 1.563, 1.25, 1, 0.85]; // Major third 70 | 71 | export const textStyles = { 72 | title: { 73 | fontFamily: fonts.normal, 74 | fontSize: `${typeScale[0]}rem`, 75 | fontWeight: "bold", 76 | lineHeight: "1.4", 77 | [mediaQuery.small]: { 78 | fontSize: "36px", 79 | }, 80 | } as CSSObject, 81 | subtitle: { 82 | fontFamily: fonts.normal, 83 | fontSize: `${typeScale[3]}rem`, 84 | fontWeight: 500, 85 | lineHeight: "1.75", 86 | [mediaQuery.small]: { 87 | fontSize: "18px", 88 | }, 89 | } as CSSObject, 90 | heading1: { 91 | fontFamily: fonts.normal, 92 | fontSize: `${typeScale[2]}rem`, 93 | fontWeight: 500, 94 | lineHeight: "1.75", 95 | } as CSSObject, 96 | heading2: { 97 | fontFamily: fonts.normal, 98 | fontSize: `${typeScale[3]}rem`, 99 | fontWeight: 500, 100 | lineHeight: "1.75", 101 | } as CSSObject, 102 | heading3: { 103 | fontFamily: fonts.normal, 104 | fontSize: `${typeScale[4]}rem`, 105 | fontWeight: 500, 106 | lineHeight: "1.75", 107 | } as CSSObject, 108 | body: { 109 | fontFamily: fonts.normal, 110 | fontSize: `${typeScale[5]}rem`, 111 | fontWeight: 400, 112 | lineHeight: "1.75", 113 | } as CSSObject, 114 | small: { 115 | fontFamily: fonts.normal, 116 | fontSize: `${typeScale[6]}rem`, 117 | fontWeight: 400, 118 | lineHeight: "1.4", 119 | } as CSSObject, 120 | code: { 121 | fontFamily: fonts.monospace, 122 | fontSize: "90%", 123 | lineHeight: "1.5", 124 | } as CSSObject, 125 | }; 126 | 127 | export const sizes = { 128 | sidebarWidth: 260, 129 | // draggable area on frameless mac app 130 | desktopControlWindowBar: { 131 | height: 46, 132 | }, 133 | toolbar: { 134 | height: 46, 135 | itemSeparator: 8, 136 | }, 137 | spacing: { 138 | nano: 2, 139 | micro: 4, 140 | small: 8, 141 | medium: 16, 142 | large: 32, 143 | xlarge: 64, 144 | xxlarge: 128, 145 | }, 146 | }; 147 | 148 | export const light = { 149 | colors: colors, 150 | fonts: fonts, 151 | textStyles: textStyles, 152 | sizes: sizes, 153 | }; 154 | -------------------------------------------------------------------------------- /packages/editor-ui-dropdown-menu/dropdown-menu.tsx: -------------------------------------------------------------------------------- 1 | import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; 2 | import { styled, keyframes } from "@stitches/react"; 3 | 4 | const slideUpAndFade = keyframes({ 5 | "0%": { opacity: 0, transform: "translateY(2px)" }, 6 | "100%": { opacity: 1, transform: "translateY(0)" }, 7 | }); 8 | 9 | const slideRightAndFade = keyframes({ 10 | "0%": { opacity: 0, transform: "translateX(-2px)" }, 11 | "100%": { opacity: 1, transform: "translateX(0)" }, 12 | }); 13 | 14 | const slideDownAndFade = keyframes({ 15 | "0%": { opacity: 0, transform: "translateY(-2px)" }, 16 | "100%": { opacity: 1, transform: "translateY(0)" }, 17 | }); 18 | 19 | const slideLeftAndFade = keyframes({ 20 | "0%": { opacity: 0, transform: "translateX(2px)" }, 21 | "100%": { opacity: 1, transform: "translateX(0)" }, 22 | }); 23 | 24 | const StyledContent = styled(DropdownMenuPrimitive.Content, { 25 | minWidth: 220, 26 | backgroundColor: "white", 27 | borderRadius: 6, 28 | padding: 5, 29 | boxShadow: 30 | "0px 10px 38px -10px rgba(22, 23, 24, 0.35), 0px 10px 20px -15px rgba(22, 23, 24, 0.2)", 31 | "@media (prefers-reduced-motion: no-preference)": { 32 | animationDuration: "400ms", 33 | animationTimingFunction: "cubic-bezier(0.16, 1, 0.3, 1)", 34 | animationFillMode: "forwards", 35 | willChange: "transform, opacity", 36 | '&[data-state="open"]': { 37 | '&[data-side="top"]': { animationName: slideDownAndFade }, 38 | '&[data-side="right"]': { animationName: slideLeftAndFade }, 39 | '&[data-side="bottom"]': { animationName: slideUpAndFade }, 40 | '&[data-side="left"]': { animationName: slideRightAndFade }, 41 | }, 42 | }, 43 | }); 44 | 45 | const itemStyles = { 46 | all: "unset", 47 | fontSize: 13, 48 | lineHeight: 1, 49 | // color: violet.violet11, 50 | borderRadius: 3, 51 | display: "flex", 52 | gap: 4, 53 | alignItems: "center", 54 | height: 25, 55 | padding: "0 5px", 56 | position: "relative", 57 | paddingLeft: 8, 58 | userSelect: "none", 59 | 60 | "&[data-disabled]": { 61 | // color: mauve.mauve8, 62 | opacity: 0.5, 63 | pointerEvents: "none", 64 | }, 65 | 66 | "&:focus": { 67 | backgroundColor: `rgba(0, 0, 0, 0.1)`, 68 | // color: violet.violet1, 69 | }, 70 | }; 71 | 72 | const StyledItem = styled(DropdownMenuPrimitive.Item, { ...itemStyles }); 73 | const StyledCheckboxItem = styled(DropdownMenuPrimitive.CheckboxItem, { 74 | ...itemStyles, 75 | }); 76 | const StyledRadioItem = styled(DropdownMenuPrimitive.RadioItem, { 77 | ...itemStyles, 78 | }); 79 | const StyledTriggerItem = styled(DropdownMenuPrimitive.SubTrigger, { 80 | '&[data-state="open"]': { 81 | // backgroundColor: violet.violet4, 82 | // color: violet.violet11, 83 | }, 84 | ...itemStyles, 85 | }); 86 | 87 | const StyledLabel = styled(DropdownMenuPrimitive.Label, { 88 | paddingLeft: 25, 89 | fontSize: 12, 90 | lineHeight: "25px", 91 | // color: mauve.mauve11, 92 | }); 93 | 94 | const StyledSeparator = styled(DropdownMenuPrimitive.Separator, { 95 | height: 1, 96 | backgroundColor: "rgba(0, 0, 0, 0.2)", 97 | margin: 5, 98 | }); 99 | 100 | const StyledItemIndicator = styled(DropdownMenuPrimitive.ItemIndicator, { 101 | position: "absolute", 102 | left: 0, 103 | width: 25, 104 | display: "inline-flex", 105 | alignItems: "center", 106 | justifyContent: "center", 107 | }); 108 | 109 | const StyledArrow = styled(DropdownMenuPrimitive.Arrow, { 110 | fill: "white", 111 | }); 112 | 113 | // Exports 114 | export const DropdownMenu = DropdownMenuPrimitive.Root; 115 | export const DropdownMenuPortal = DropdownMenuPrimitive.Portal; 116 | export const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; 117 | export const DropdownMenuContent = StyledContent; 118 | export const DropdownMenuItem = StyledItem; 119 | export const DropdownMenuCheckboxItem = StyledCheckboxItem; 120 | export const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; 121 | export const DropdownMenuRadioItem = StyledRadioItem; 122 | export const DropdownMenuItemIndicator = StyledItemIndicator; 123 | export const DropdownMenuTriggerItem = StyledTriggerItem; 124 | export const DropdownMenuLabel = StyledLabel; 125 | export const DropdownMenuSeparator = StyledSeparator; 126 | export const DropdownMenuArrow = StyledArrow; 127 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-input-select/property-input-select.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styled from "@emotion/styled"; 3 | import * as Select from "@radix-ui/react-select"; 4 | import { 5 | CheckIcon, 6 | ChevronDownIcon, 7 | ChevronUpIcon, 8 | } from "@radix-ui/react-icons"; 9 | import { css } from "@emotion/react"; 10 | 11 | type OptionsInput = Array<{ label: string; value: string }> | Array; 12 | 13 | interface SelectInputProps { 14 | autofocus?: boolean; 15 | value?: string; 16 | placeholder?: string; 17 | options?: OptionsInput; 18 | 19 | readonly?: boolean; 20 | disabled?: boolean; 21 | 22 | onChange?: (value: string) => void; 23 | onClick?: () => void; 24 | 25 | // aria 26 | ariaLabel?: string; 27 | } 28 | 29 | export function PropertySelectInput({ 30 | autofocus = false, 31 | placeholder, 32 | options: _options = [], 33 | readonly, 34 | disabled, 35 | value: initial, 36 | ariaLabel, 37 | onChange, 38 | }: SelectInputProps) { 39 | const [value, setValue] = React.useState(initial); 40 | const options = React.useMemo( 41 | () => 42 | _options.map((o) => { 43 | if (typeof o === "string") { 44 | return { 45 | label: o, 46 | value: o, 47 | }; 48 | } 49 | return o; 50 | }), 51 | [_options] 52 | ); 53 | 54 | return ( 55 | { 57 | setValue(v); 58 | onChange?.(v); 59 | }} 60 | value={value} 61 | disabled={disabled} 62 | > 63 | 68 | 69 | 70 | 71 | 72 | 73 | 78 | 79 | 80 | 81 | 82 | 83 | {options.map(({ value, label }) => ( 84 | 85 | {label} 86 | 87 | ))} 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | ); 96 | } 97 | 98 | const SelectItem = React.forwardRef( 99 | ( 100 | { children, ...props }: React.PropsWithChildren, 101 | forwardedRef: React.Ref 102 | ) => { 103 | return ( 104 | 105 | {children} 106 | 107 | 108 | 109 | 110 | ); 111 | } 112 | ); 113 | 114 | const StyledSelectItem = styled(Select.Item)` 115 | font-family: sans-serif; 116 | font-size: 11px; 117 | line-height: 1; 118 | color: #1a1a1a; 119 | border-radius: 3px; 120 | display: flex; 121 | align-items: center; 122 | height: 25px; 123 | padding: 0 35px 0 25px; 124 | position: relative; 125 | user-select: none; 126 | 127 | &[data-disabled] { 128 | color: rgba(0, 0, 0, 0.2); 129 | pointer-events: none; 130 | } 131 | &[data-highlighted] { 132 | outline: none; 133 | background-color: rgba(0, 0, 0, 0.1); 134 | color: var(--violet1); 135 | } 136 | 137 | .indicator { 138 | position: absolute; 139 | left: 0; 140 | width: 25px; 141 | display: inline-flex; 142 | align-items: center; 143 | justify-content: center; 144 | } 145 | 146 | /* */ 147 | `; 148 | 149 | const StyledTrigger = styled(Select.Trigger)` 150 | all: unset; 151 | font-family: sans-serif; 152 | display: inline-flex; 153 | align-items: center; 154 | justify-content: center; 155 | border-radius: 4px; 156 | padding: 0 8px; 157 | font-size: 11px; 158 | line-height: 1; 159 | height: 24px; 160 | gap: 4px; 161 | background-color: white; 162 | color: #1a1a1a; 163 | box-shadow: 0 2px 2px rgba(22, 23, 24, 0.1); 164 | 165 | &:hover { 166 | background-color: #f2f2f2; 167 | } 168 | 169 | &:focus { 170 | box-shadow: 0 0 0 1px black; 171 | } 172 | 173 | &[data-placeholder] { 174 | color: #3c3c3c; 175 | } 176 | 177 | .icon { 178 | color: #1a1a1a; 179 | } 180 | `; 181 | 182 | const SelectScrollButton = css` 183 | display: flex; 184 | align-items: center; 185 | justify-content: center; 186 | height: 25px; 187 | background-color: white; 188 | color: #1a1a1a; 189 | cursor: default; 190 | `; 191 | 192 | const SelectScrollUpButton = styled(Select.ScrollUpButton)` 193 | ${SelectScrollButton} 194 | `; 195 | 196 | const SelectScrollDownButton = styled(Select.ScrollDownButton)` 197 | ${SelectScrollButton} 198 | `; 199 | 200 | const SelectContent = styled(Select.Content)` 201 | overflow: hidden; 202 | background-color: white; 203 | border-radius: 6px; 204 | box-shadow: 0px 10px 38px -10px rgba(22, 23, 24, 0.35), 205 | 0px 10px 20px -15px rgba(22, 23, 24, 0.2); 206 | `; 207 | -------------------------------------------------------------------------------- /packages/editor-ui-property/property-input/property-input.tsx: -------------------------------------------------------------------------------- 1 | import styled from "../theme/styled"; 2 | import React, { useEffect } from "react"; 3 | 4 | import { PropertyCell } from "../property-cell"; 5 | import { useTheme } from "../theme"; 6 | import { input_type_number_disable_browser_default_appearence } from "../css"; 7 | import { css } from "@emotion/react"; 8 | 9 | type PropertyInputType = "number" | "text" | "select"; 10 | 11 | export type PropertyInputProps = PlainInputProps; 12 | 13 | interface PlainInputProps { 14 | autofocus?: boolean; 15 | prefix?: React.ReactNode; 16 | suffix?: React.ReactNode; 17 | placeholder?: string; 18 | type?: PropertyInputType; 19 | value?: string | number; 20 | min?: number; 21 | max?: number; 22 | readonly?: boolean; 23 | disabled?: boolean; 24 | stopPropagation?: boolean; 25 | 26 | /** 27 | * The pattern attribute specifies a regular expression the form control's value should match. If a non-null value doesn't conform to the constraints set by the pattern value, the ValidityState object's read-only patternMismatch property will be true. 28 | */ 29 | pattern?: string; 30 | onChange?: (value: string) => void; 31 | onClick?: () => void; 32 | } 33 | 34 | export function PropertyInput({ 35 | autofocus = false, 36 | prefix, 37 | suffix, 38 | placeholder, 39 | type = "text", 40 | value: initial = "", 41 | min, 42 | max, 43 | readonly, 44 | disabled, 45 | pattern, 46 | stopPropagation, 47 | onChange, 48 | onClick, 49 | }: PropertyInputProps) { 50 | const [focused, setFocused] = React.useState(false); 51 | const [value, setValue] = React.useState(initial); 52 | const theme = useTheme(); 53 | const inputRef = React.useRef(null); 54 | 55 | useEffect(() => { 56 | setValue(initial); 57 | }, [initial]); 58 | 59 | const onclick = (e) => { 60 | if (inputRef.current) { 61 | inputRef.current.focus(); 62 | } 63 | onClick?.(); 64 | e.stopPropagation(); 65 | }; 66 | 67 | const onvalue = (value: string) => { 68 | setValue(value); 69 | onChange?.(value); 70 | }; 71 | 72 | const onfocus = () => { 73 | setFocused(true); 74 | }; 75 | 76 | const onfocuscapture = () => { 77 | // select all text on focus 78 | inputRef.current?.select(); 79 | }; 80 | 81 | const onblur = () => { 82 | setFocused(false); 83 | }; 84 | 85 | const onkeydown = (e: React.KeyboardEvent) => { 86 | if (e.key === "Enter" || e.key === "Escape") { 87 | inputRef.current?.blur(); 88 | } 89 | 90 | if (readonly) { 91 | return; 92 | } 93 | 94 | // up or down key ally 95 | if (type === "number" && (e.key === "ArrowUp" || e.key === "ArrowDown")) { 96 | e.preventDefault(); // this is required to prevent number's default behavior 97 | // if shift key is pressed, increase or decrease by 10 98 | // if ctrl / cmd key is pressed, increase or decrease by 0.1 99 | // if both shift and ctrl / cmd key is pressed, increase or decrease by 100 100 | const step = e.shiftKey 101 | ? e.ctrlKey || e.metaKey 102 | ? 100 103 | : 10 104 | : e.ctrlKey || e.metaKey 105 | ? 0.1 106 | : 1; 107 | 108 | const value = Number(inputRef.current?.value); 109 | const newValue = e.key === "ArrowUp" ? value + step : value - step; 110 | onvalue("" + newValue); 111 | } 112 | 113 | if (stopPropagation) { 114 | e.stopPropagation(); 115 | } 116 | }; 117 | 118 | return ( 119 | 126 | {prefix && {prefix}} 127 | onvalue(e.target.value)} 139 | placeholder={placeholder} 140 | pattern={pattern} 141 | min={min} 142 | max={max} 143 | style={{ 144 | margin: 0, 145 | marginLeft: !prefix ? 8 : 0, 146 | marginRight: !suffix ? 8 : 0, 147 | }} 148 | /> 149 | {suffix && {suffix}} 150 | 151 | ); 152 | } 153 | 154 | export function PropertyNumericInput({ 155 | onChange, 156 | ...props 157 | }: Omit, "type" | "onChange"> & { 158 | onChange?: (value: number) => void; 159 | }) { 160 | return ( 161 | { 165 | const val = Number(txt); 166 | if (isNaN(val)) { 167 | return; 168 | } 169 | onChange?.(val); 170 | }} 171 | /> 172 | ); 173 | } 174 | 175 | const PlainInput = styled.input` 176 | width: 100%; 177 | height: 100%; 178 | border: none; 179 | outline: none; 180 | background: transparent; 181 | color: ${(props) => 182 | /* @ts-ignore */ 183 | props.theme.input.value}; 184 | font-size: 11px; 185 | font-weight: 500; 186 | padding: 0; 187 | 188 | &:disabled { 189 | color: ${(props) => 190 | // @ts-ignore 191 | props.theme.input.value_disabled}; 192 | } 193 | 194 | ::placeholder { 195 | color: #999; 196 | } 197 | 198 | ${input_type_number_disable_browser_default_appearence} 199 | `; 200 | 201 | const _fix = css` 202 | cursor: default; 203 | flex-shrink: 0; 204 | display: flex; 205 | align-items: center; 206 | justify-content: center; 207 | font-family: Inter, sans-serif; 208 | font-size: 9px; 209 | height: 24px; 210 | width: 24px; 211 | `; 212 | 213 | const Prefix = styled.span` 214 | ${_fix} 215 | color: ${(props) => 216 | // @ts-ignore 217 | props.theme.input.prefix}; 218 | `; 219 | 220 | const Suffix = styled.span` 221 | ${_fix} 222 | color: ${(props) => 223 | // @ts-ignore 224 | props.theme.input.suffix}; 225 | `; 226 | -------------------------------------------------------------------------------- /packages/editor-ui-hooks/use-hover.ts: -------------------------------------------------------------------------------- 1 | // From: https://github.com/radix-ui/primitives/blob/main/packages/react/scroll-area/src/useHover.ts 2 | // MIT License 3 | 4 | // This code is a fork of `react-aria`'s `useHover` hook. The code in this file are based on code 5 | // from both `react-aria` and `react-interactions`. Original licensing for each project can be found 6 | // in the the root directories of each respective repository. 7 | // 8 | // @see https://github.com/adobe/react-spectrum/tree/452d1cb6a49f9f493757737edaf2b64014108de6/packages/%40react-aria 9 | // @see https://github.com/facebook/react/tree/cc7c1aece46a6b69b41958d731e0fd27c94bfc6c/packages/react-interactions 10 | 11 | import * as React from "react"; 12 | 13 | export interface HoverEvent { 14 | /** The type of hover event being fired. */ 15 | type: "hoverstart" | "hoverend"; 16 | /** The pointer type that triggered the hover event. */ 17 | pointerType: "mouse" | "pen"; 18 | /** The target element of the hover event. */ 19 | target: HTMLElement; 20 | } 21 | 22 | export interface HoverEvents { 23 | /** Handler that is called when a hover interaction starts. */ 24 | onHoverStart?(e: HoverEvent): void; 25 | /** Handler that is called when a hover interaction ends. */ 26 | onHoverEnd?(e: HoverEvent): void; 27 | /** Handler that is called when the hover state changes. */ 28 | onHoverChange?(isHovering: boolean): void; 29 | } 30 | 31 | export interface HoverProps extends HoverEvents { 32 | /** Whether the hover events should be disabled. */ 33 | isDisabled?: boolean; 34 | } 35 | 36 | interface HoverResult { 37 | /** Props to spread on the target element. */ 38 | hoverProps: React.HTMLAttributes; 39 | isHovered: boolean; 40 | } 41 | 42 | // iOS fires onPointerEnter twice: once with pointerType="touch" and again with pointerType="mouse". 43 | // We want to ignore these emulated events so they do not trigger hover behavior. 44 | // See https://bugs.webkit.org/show_bug.cgi?id=214609. 45 | let globalIgnoreEmulatedMouseEvents = false; 46 | let hoverCount = 0; 47 | 48 | function setGlobalIgnoreEmulatedMouseEvents() { 49 | globalIgnoreEmulatedMouseEvents = true; 50 | 51 | // Clear globalIgnoreEmulatedMouseEvents after a short timeout. iOS fires onPointerEnter with 52 | // pointerType="mouse" immediately after onPointerUp and before onFocus. On other devices that 53 | // don't have this quirk, we don't want to ignore a mouse hover sometime in the distant future 54 | // because a user previously touched the element. 55 | setTimeout(function clearGlobalIgnoreEmulatedMouseEvents() { 56 | globalIgnoreEmulatedMouseEvents = false; 57 | }, 50); 58 | } 59 | 60 | function handleGlobalPointerEvent(event: PointerEvent) { 61 | if (event.pointerType === "touch") { 62 | setGlobalIgnoreEmulatedMouseEvents(); 63 | } 64 | } 65 | 66 | function setupGlobalTouchEvents() { 67 | if (typeof document === "undefined") { 68 | return; 69 | } 70 | 71 | document.addEventListener("pointerup", handleGlobalPointerEvent); 72 | 73 | hoverCount++; 74 | return function teardownGlobalTouchEvents() { 75 | hoverCount--; 76 | if (hoverCount > 0) { 77 | return; 78 | } 79 | document.removeEventListener("pointerup", handleGlobalPointerEvent); 80 | }; 81 | } 82 | 83 | /** 84 | * Handles pointer hover interactions for an element. Normalizes behavior 85 | * across browsers and platforms, and ignores emulated mouse events on touch devices. 86 | */ 87 | export function useHover(props: HoverProps = {}): HoverResult { 88 | const { onHoverStart, onHoverChange, onHoverEnd, isDisabled } = props; 89 | 90 | const [isHovered, setHovered] = React.useState(false); 91 | const state = React.useRef({ 92 | isHovered: false, 93 | ignoreEmulatedMouseEvents: false, 94 | }).current; 95 | 96 | React.useEffect(setupGlobalTouchEvents, []); 97 | 98 | const hoverProps = React.useMemo( 99 | function getHoverProps() { 100 | function triggerHoverStart( 101 | event: 102 | | PointerEvent 103 | | React.PointerEvent 104 | | MouseEvent 105 | | React.MouseEvent, 106 | pointerType: "mouse" | "pen" | "touch" 107 | ) { 108 | if (isDisabled || pointerType === "touch" || state.isHovered) { 109 | return; 110 | } 111 | 112 | state.isHovered = true; 113 | const target = event.target as HTMLElement; 114 | 115 | if (onHoverStart) { 116 | onHoverStart({ 117 | type: "hoverstart", 118 | target, 119 | pointerType, 120 | }); 121 | } 122 | 123 | if (onHoverChange) { 124 | onHoverChange(true); 125 | } 126 | 127 | setHovered(true); 128 | } 129 | 130 | function triggerHoverEnd( 131 | event: 132 | | PointerEvent 133 | | React.PointerEvent 134 | | MouseEvent 135 | | React.MouseEvent, 136 | pointerType: "mouse" | "pen" | "touch" 137 | ) { 138 | if (isDisabled || pointerType === "touch" || !state.isHovered) { 139 | return; 140 | } 141 | 142 | state.isHovered = false; 143 | const target = event.target as HTMLElement; 144 | 145 | if (onHoverEnd) { 146 | onHoverEnd({ 147 | type: "hoverend", 148 | target, 149 | pointerType, 150 | }); 151 | } 152 | 153 | if (onHoverChange) { 154 | onHoverChange(false); 155 | } 156 | 157 | setHovered(false); 158 | } 159 | 160 | const hoverProps: React.HTMLAttributes = { 161 | onPointerEnter(event: React.PointerEvent) { 162 | if ( 163 | globalIgnoreEmulatedMouseEvents && 164 | event.pointerType === "mouse" 165 | ) { 166 | return; 167 | } 168 | triggerHoverStart(event, event.pointerType); 169 | }, 170 | onPointerLeave(event: React.PointerEvent) { 171 | triggerHoverEnd(event, event.pointerType); 172 | }, 173 | }; 174 | 175 | return hoverProps; 176 | }, 177 | [isDisabled, state, onHoverStart, onHoverChange, onHoverEnd] 178 | ); 179 | 180 | return { 181 | hoverProps, 182 | isHovered, 183 | }; 184 | } 185 | -------------------------------------------------------------------------------- /packages/editor-ui-sortable/sortable.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | closestCenter, 3 | DndContext, 4 | DragEndEvent, 5 | DragMoveEvent, 6 | DragOverlay, 7 | DragStartEvent, 8 | PointerSensor, 9 | Translate, 10 | useSensor, 11 | useSensors, 12 | } from "@dnd-kit/core"; 13 | import { 14 | SortableContext, 15 | useSortable, 16 | verticalListSortingStrategy, 17 | } from "@dnd-kit/sortable"; 18 | import React, { 19 | createContext, 20 | memo, 21 | ReactNode, 22 | Ref, 23 | useCallback, 24 | useContext, 25 | useMemo, 26 | useRef, 27 | useState, 28 | } from "react"; 29 | import { createPortal } from "react-dom"; 30 | 31 | export type RelativeDropPosition = "above" | "below" | "inside"; 32 | 33 | export type DropValidator = ( 34 | sourceId: string, 35 | destinationId: string, 36 | position: RelativeDropPosition 37 | ) => boolean; 38 | 39 | const defaultAcceptsDrop: DropValidator = ( 40 | sourceId, 41 | destinationId, 42 | position 43 | ) => { 44 | return position !== "inside" && sourceId !== destinationId; 45 | }; 46 | 47 | const SortableItemContext = createContext<{ 48 | position: Translate; 49 | acceptsDrop: DropValidator; 50 | setActivatorEvent: (event: PointerEvent) => void; 51 | }>({ 52 | position: { x: 0, y: 0 }, 53 | acceptsDrop: defaultAcceptsDrop, 54 | setActivatorEvent: () => {}, 55 | }); 56 | 57 | function validateDropIndicator( 58 | acceptsDrop: DropValidator, 59 | activeId: string, 60 | overId: string, 61 | offsetTop: number, 62 | elementTop: number, 63 | elementHeight: number 64 | ): RelativeDropPosition | undefined { 65 | const acceptsDropInside = acceptsDrop(activeId, overId, "inside"); 66 | 67 | // If we're in the center of the element, prefer dropping inside 68 | if ( 69 | offsetTop >= elementTop + elementHeight / 3 && 70 | offsetTop <= elementTop + (elementHeight * 2) / 3 && 71 | acceptsDropInside 72 | ) 73 | return "inside"; 74 | 75 | // Are we over the top or bottom half of the element? 76 | const indicator = 77 | offsetTop < elementTop + elementHeight / 2 ? "above" : "below"; 78 | 79 | // Drop above or below if possible, falling back to inside 80 | return acceptsDrop(activeId, overId, indicator) 81 | ? indicator 82 | : acceptsDropInside 83 | ? "inside" 84 | : undefined; 85 | } 86 | 87 | /* ---------------------------------------------------------------------------- 88 | * Item 89 | * ------------------------------------------------------------------------- */ 90 | 91 | type UseSortableReturnType = ReturnType; 92 | 93 | interface ItemProps { 94 | id: string; 95 | disabled?: boolean; 96 | children: ( 97 | props: { 98 | ref: Ref; 99 | relativeDropPosition?: RelativeDropPosition; 100 | [key: string]: any; 101 | } & UseSortableReturnType["attributes"] 102 | ) => JSX.Element; 103 | } 104 | 105 | function SortableItem({ 106 | id, 107 | disabled, 108 | children, 109 | }: ItemProps) { 110 | const { position, acceptsDrop, setActivatorEvent } = 111 | useContext(SortableItemContext); 112 | const sortable = useSortable({ id, disabled }); 113 | 114 | const { 115 | active, 116 | activatorEvent, 117 | attributes, 118 | listeners, 119 | setNodeRef, 120 | isDragging, 121 | index, 122 | overIndex, 123 | over, 124 | } = sortable; 125 | 126 | if (activatorEvent) { 127 | setActivatorEvent(activatorEvent as PointerEvent); 128 | } 129 | 130 | const eventY = (activatorEvent as PointerEvent | null)?.clientY ?? 0; 131 | const offsetTop = eventY + position.y; 132 | 133 | const ref = useCallback((node: T) => setNodeRef(node), [setNodeRef]); 134 | 135 | return children({ 136 | ref, 137 | ...attributes, 138 | ...listeners, 139 | relativeDropPosition: 140 | index === overIndex && !isDragging && active && over 141 | ? validateDropIndicator( 142 | acceptsDrop, 143 | active.id, 144 | over.id, 145 | offsetTop, 146 | over.rect.offsetTop, 147 | over.rect.height 148 | ) 149 | : undefined, 150 | }); 151 | } 152 | 153 | /* ---------------------------------------------------------------------------- 154 | * Root 155 | * ------------------------------------------------------------------------- */ 156 | 157 | interface RootProps { 158 | keys: string[]; 159 | children: ReactNode; 160 | renderOverlay?: (index: number) => ReactNode; 161 | onMoveItem?: ( 162 | sourceIndex: number, 163 | destinationIndex: number, 164 | position: RelativeDropPosition 165 | ) => void; 166 | acceptsDrop?: DropValidator; 167 | } 168 | 169 | function SortableRoot({ 170 | keys, 171 | children, 172 | onMoveItem, 173 | renderOverlay, 174 | acceptsDrop = defaultAcceptsDrop, 175 | }: RootProps) { 176 | const [_document, set_document] = React.useState(null); 177 | React.useEffect(() => { 178 | set_document(document); 179 | }, []); 180 | 181 | const sensors = useSensors( 182 | useSensor(PointerSensor, { 183 | activationConstraint: { 184 | distance: 4, 185 | }, 186 | }) 187 | ); 188 | 189 | const [activeIndex, setActiveIndex] = useState(); 190 | const activatorEvent = useRef(null); 191 | 192 | const setActivatorEvent = useCallback((event: PointerEvent) => { 193 | activatorEvent.current = event; 194 | }, []); 195 | 196 | const [position, setPosition] = useState({ x: 0, y: 0 }); 197 | 198 | const handleDragStart = useCallback( 199 | (event: DragStartEvent) => { 200 | setActiveIndex(keys.indexOf(event.active.id)); 201 | }, 202 | [keys] 203 | ); 204 | 205 | const handleDragMove = useCallback((event: DragMoveEvent) => { 206 | setPosition({ ...event.delta }); 207 | }, []); 208 | 209 | const handleDragEnd = useCallback( 210 | (event: DragEndEvent) => { 211 | const { active, over } = event; 212 | 213 | setActiveIndex(undefined); 214 | 215 | if (over && active.id !== over.id) { 216 | const oldIndex = keys.indexOf(active.id); 217 | const newIndex = keys.indexOf(over.id); 218 | 219 | const eventY = activatorEvent.current?.clientY ?? 0; 220 | const offsetTop = eventY + position.y; 221 | 222 | const indicator = validateDropIndicator( 223 | acceptsDrop, 224 | active.id, 225 | over.id, 226 | offsetTop, 227 | over.rect.offsetTop, 228 | over.rect.height 229 | ); 230 | 231 | if (!indicator) return; 232 | 233 | onMoveItem?.(oldIndex, newIndex, indicator); 234 | } 235 | }, 236 | [acceptsDrop, keys, onMoveItem, position.y] 237 | ); 238 | 239 | return ( 240 | ({ 243 | acceptsDrop, 244 | position, 245 | setActivatorEvent, 246 | }), 247 | [acceptsDrop, position, setActivatorEvent] 248 | )} 249 | > 250 | {/* @ts-ignore */} 251 | 258 | 259 | {children} 260 | 261 | {renderOverlay && 262 | _document && 263 | createPortal( 264 | 265 | {activeIndex !== undefined && renderOverlay(activeIndex)} 266 | , 267 | _document.body 268 | )} 269 | 270 | 271 | ); 272 | } 273 | 274 | export const Item = memo(SortableItem); 275 | export const Root = memo(SortableRoot); 276 | -------------------------------------------------------------------------------- /packages/editor-ui-icons/icons.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface IconListProps { 4 | width: number; 5 | height: number; 6 | svg: JSX.Element; 7 | } 8 | 9 | export interface IconList { 10 | indicator: IconListProps; 11 | hirachyLayout: IconListProps; 12 | hirachyText: IconListProps; 13 | hirachyImage: IconListProps; 14 | pipette: IconListProps; 15 | headerInsert: IconListProps; 16 | headerScreen: IconListProps; 17 | headerFrame: IconListProps; 18 | headerText: IconListProps; 19 | headerScroll: IconListProps; 20 | headerPage: IconListProps; 21 | headerComponent: IconListProps; 22 | headerVariables: IconListProps; 23 | headerHandoff: IconListProps; 24 | headerZoom: IconListProps; 25 | xPostion: IconListProps; 26 | yPostion: IconListProps; 27 | close: IconListProps; 28 | allBoundary: IconListProps; 29 | cornerBoundary: IconListProps; 30 | } 31 | 32 | const icons: IconList = { 33 | indicator: { 34 | width: 12, 35 | height: 12, 36 | svg: , 37 | }, 38 | hirachyLayout: { 39 | width: 12, 40 | height: 12, 41 | svg: ( 42 | 48 | ), 49 | }, 50 | hirachyText: { 51 | width: 12, 52 | height: 12, 53 | svg: ( 54 | 60 | ), 61 | }, 62 | hirachyImage: { 63 | width: 12, 64 | height: 12, 65 | svg: ( 66 | 70 | ), 71 | }, 72 | pipette: { 73 | width: 16, 74 | height: 16, 75 | svg: ( 76 | 80 | ), 81 | }, 82 | headerComponent: { 83 | width: 24, 84 | height: 24, 85 | svg: ( 86 | 92 | ), 93 | }, 94 | headerFrame: { 95 | width: 24, 96 | height: 24, 97 | svg: , 98 | }, 99 | headerHandoff: { 100 | width: 24, 101 | height: 24, 102 | svg: ( 103 | 109 | ), 110 | }, 111 | headerInsert: { 112 | width: 24, 113 | height: 24, 114 | svg: , 115 | }, 116 | headerPage: { 117 | width: 24, 118 | height: 24, 119 | svg: ( 120 | <> 121 | 122 | 126 | 127 | ), 128 | }, 129 | headerScreen: { 130 | width: 24, 131 | height: 24, 132 | svg: ( 133 | <> 134 | 135 | 139 | 140 | ), 141 | }, 142 | headerScroll: { 143 | width: 24, 144 | height: 24, 145 | svg: ( 146 | <> 147 | 148 | 152 | 156 | 157 | ), 158 | }, 159 | headerText: { 160 | width: 24, 161 | height: 24, 162 | svg: ( 163 | 169 | ), 170 | }, 171 | headerVariables: { 172 | width: 24, 173 | height: 24, 174 | svg: ( 175 | 181 | ), 182 | }, 183 | headerZoom: { 184 | width: 24, 185 | height: 24, 186 | svg: ( 187 | 191 | ), 192 | }, 193 | xPostion: { 194 | width: 16, 195 | height: 16, 196 | svg: ( 197 | 201 | ), 202 | }, 203 | yPostion: { 204 | width: 16, 205 | height: 16, 206 | svg: ( 207 | 211 | ), 212 | }, 213 | close: { 214 | width: 16, 215 | height: 16, 216 | svg: ( 217 | 221 | ), 222 | }, 223 | allBoundary: { 224 | width: 17, 225 | height: 17, 226 | svg: ( 227 | 231 | ), 232 | }, 233 | cornerBoundary: { 234 | width: 17, 235 | height: 17, 236 | svg: ( 237 | 241 | ), 242 | }, 243 | }; 244 | 245 | export default icons; 246 | --------------------------------------------------------------------------------