From fe82b159c52158eef61820d04dd2b7302a29bed9 Mon Sep 17 00:00:00 2001 From: Sidney Alcantara Date: Wed, 20 Oct 2021 17:15:49 +1100 Subject: [PATCH] consolidate CodeEditor to components/CodeEditor, allow resize, use github themes --- package.json | 2 +- src/assets/icons/ResizeBottomRight.tsx | 10 + src/components/CodeEditor.tsx | 114 ---- .../{ => CodeEditor}/CodeEditorHelper.tsx | 0 src/components/CodeEditor/extensions.d.ts | 97 ++++ .../components/CodeEditor/firebaseAuth.d.ts | 0 .../CodeEditor/firebaseStorage.d.ts | 0 .../components/CodeEditor}/firestore.d.ts | 6 +- .../CodeEditor/github-dark-default.json | 535 ++++++++++++++++++ .../CodeEditor/github-light-default.json | 531 +++++++++++++++++ src/components/CodeEditor/index.tsx | 232 ++++++++ src/components/CodeEditor/utils.d.ts | 50 ++ .../FieldSettings/DefaultValueInput.tsx | 54 +- src/components/Table/Settings/Webhooks.tsx | 6 +- .../Extensions/Step3Conditions.tsx | 22 +- .../TableHeader/Extensions/Step4Body.tsx | 21 +- src/components/Table/editors/CodeEditor.tsx | 398 ------------- src/components/fields/Action/Settings.tsx | 19 +- src/components/fields/Aggregate/Settings.tsx | 11 +- .../fields/Code/SideDrawerField.tsx | 30 +- src/components/fields/Derivative/Settings.tsx | 12 +- types/custom.ts | 4 + yarn.lock | 2 +- 23 files changed, 1526 insertions(+), 630 deletions(-) create mode 100644 src/assets/icons/ResizeBottomRight.tsx delete mode 100644 src/components/CodeEditor.tsx rename src/components/{ => CodeEditor}/CodeEditorHelper.tsx (100%) create mode 100644 src/components/CodeEditor/extensions.d.ts rename public/auth.d.ts => src/components/CodeEditor/firebaseAuth.d.ts (100%) rename public/storage.d.ts => src/components/CodeEditor/firebaseStorage.d.ts (100%) rename {public => src/components/CodeEditor}/firestore.d.ts (99%) create mode 100644 src/components/CodeEditor/github-dark-default.json create mode 100644 src/components/CodeEditor/github-light-default.json create mode 100644 src/components/CodeEditor/index.tsx create mode 100644 src/components/CodeEditor/utils.d.ts delete mode 100644 src/components/Table/editors/CodeEditor.tsx diff --git a/package.json b/package.json index b30884a2..ad8cf3cc 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "@emotion/styled": "^11.3.0", "@hookform/resolvers": "^2.8.1", "@mdi/js": "^6.2.95", - "@monaco-editor/react": "^4.1.0", + "@monaco-editor/react": "^4.3.1", "@mui/icons-material": "^5.0.0", "@mui/lab": "^5.0.0-alpha.50", "@mui/material": "^5.0.0", diff --git a/src/assets/icons/ResizeBottomRight.tsx b/src/assets/icons/ResizeBottomRight.tsx new file mode 100644 index 00000000..83e967bc --- /dev/null +++ b/src/assets/icons/ResizeBottomRight.tsx @@ -0,0 +1,10 @@ +import SvgIcon, { SvgIconProps } from "@mui/material/SvgIcon"; +import { mdiResizeBottomRight } from "@mdi/js"; + +export default function ResizeBottomRight(props: SvgIconProps) { + return ( + + + + ); +} diff --git a/src/components/CodeEditor.tsx b/src/components/CodeEditor.tsx deleted file mode 100644 index 0c02862b..00000000 --- a/src/components/CodeEditor.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import React, { useRef, useMemo, useState } from "react"; -import clsx from "clsx"; -import Editor, { useMonaco } from "@monaco-editor/react"; - -import { makeStyles, createStyles } from "@mui/styles"; -import { useTheme } from "@mui/material"; - -import { useProjectContext } from "contexts/ProjectContext"; - -const useStyles = makeStyles((theme) => - createStyles({ - editorWrapper: { position: "relative" }, - resizeIcon: { - position: "absolute", - bottom: 0, - right: 0, - color: theme.palette.text.disabled, - }, - saveButton: { - marginTop: theme.spacing(1), - }, - }) -); - -export interface ICodeEditorProps { - onChange: (value: string) => void; - value: string; - height?: number; - wrapperProps?: Partial>; - disabled?: boolean; - editorOptions?: any; -} - -export default function CodeEditor({ - onChange, - value, - height = 400, - wrapperProps, - disabled, - editorOptions, -}: ICodeEditorProps) { - const theme = useTheme(); - const [initialEditorValue] = useState(value ?? ""); - const { tableState } = useProjectContext(); - const classes = useStyles(); - const monacoInstance = useMonaco(); - - const editorRef = useRef(); - - function handleEditorDidMount(_, editor) { - editorRef.current = editor; - } - - const themeTransformer = (theme: string) => { - switch (theme) { - case "dark": - return "vs-dark"; - default: - return theme; - } - }; - - useMemo(async () => { - if (!monacoInstance) { - // useMonaco returns a monaco instance but initialisation is done asynchronously - // dont execute the logic until the instance is initialised - return; - } - - try { - monacoInstance.languages.typescript.javascriptDefaults.setDiagnosticsOptions( - { - noSemanticValidation: true, - noSyntaxValidation: false, - } - ); - // compiler options - monacoInstance.languages.typescript.javascriptDefaults.setCompilerOptions( - { - target: monacoInstance.languages.typescript.ScriptTarget.ES5, - allowNonTsExtensions: true, - } - ); - } catch (error) { - console.error( - "An error occurred during initialization of Monaco: ", - error - ); - } - }, [tableState?.columns]); - - return ( -
- -
- ); -} diff --git a/src/components/CodeEditorHelper.tsx b/src/components/CodeEditor/CodeEditorHelper.tsx similarity index 100% rename from src/components/CodeEditorHelper.tsx rename to src/components/CodeEditor/CodeEditorHelper.tsx diff --git a/src/components/CodeEditor/extensions.d.ts b/src/components/CodeEditor/extensions.d.ts new file mode 100644 index 00000000..7f357e8d --- /dev/null +++ b/src/components/CodeEditor/extensions.d.ts @@ -0,0 +1,97 @@ +type Trigger = "create" | "update" | "delete"; +type Triggers = Trigger[]; + +// function types that defines extension body and should run +type Condition = + | boolean + | ((data: ExtensionContext) => boolean | Promise); + +// the argument that the extension body takes in +type ExtensionContext = { + row: Row; + ref: FirebaseFirestore.DocumentReference; + storage: firebasestorage.Storage; + db: FirebaseFirestore.Firestore; + auth: adminauth.BaseAuth; + change: any; + triggerType: Triggers; + fieldTypes: any; + extensionConfig: { + label: string; + type: string; + triggers: Trigger[]; + conditions: Condition; + requiredFields: string[]; + extensionBody: any; + }; + utilFns: any; +}; + +// extension body definition +type slackEmailBody = { + channels?: string[]; + text?: string; + emails: string[]; + blocks?: object[]; + attachments?: any; +}; + +type slackChannelBody = { + channels: string[]; + text?: string; + emails?: string[]; + blocks?: object[]; + attachments?: any; +}; + +type DocSyncBody = (context: ExtensionContext) => Promise<{ + fieldsToSync: Fields; + row: Row; + targetPath: string; +}>; + +type HistorySnapshotBody = (context: ExtensionContext) => Promise<{ + trackedFields: Fields; +}>; + +type AlgoliaIndexBody = (context: ExtensionContext) => Promise<{ + fieldsToSync: Fields; + index: string; + row: Row; + objectID: string; +}>; + +type MeiliIndexBody = (context: ExtensionContext) => Promise<{ + fieldsToSync: Fields; + index: string; + row: Row; + objectID: string; +}>; + +type BigqueryIndexBody = (context: ExtensionContext) => Promise<{ + fieldsToSync: Fields; + index: string; + row: Row; + objectID: string; +}>; + +type SlackMessageBody = ( + context: ExtensionContext +) => Promise; + +type SendgridEmailBody = (context: ExtensionContext) => Promise; + +type ApiCallBody = (context: ExtensionContext) => Promise<{ + body: string; + url: string; + method: string; + callback: any; +}>; + +type TwilioMessageBody = (context: ExtensionContext) => Promise<{ + body: string; + from: string; + to: string; +}>; + +type TaskBody = (context: ExtensionContext) => Promise; diff --git a/public/auth.d.ts b/src/components/CodeEditor/firebaseAuth.d.ts similarity index 100% rename from public/auth.d.ts rename to src/components/CodeEditor/firebaseAuth.d.ts diff --git a/public/storage.d.ts b/src/components/CodeEditor/firebaseStorage.d.ts similarity index 100% rename from public/storage.d.ts rename to src/components/CodeEditor/firebaseStorage.d.ts diff --git a/public/firestore.d.ts b/src/components/CodeEditor/firestore.d.ts similarity index 99% rename from public/firestore.d.ts rename to src/components/CodeEditor/firestore.d.ts index c840e33d..dc561cff 100644 --- a/public/firestore.d.ts +++ b/src/components/CodeEditor/firestore.d.ts @@ -844,9 +844,9 @@ declare namespace FirebaseFirestore { * `exists` property will always be true and `data()` will never return * 'undefined'. */ - export class QueryDocumentSnapshot extends DocumentSnapshot< - T - > { + export class QueryDocumentSnapshot< + T = DocumentData + > extends DocumentSnapshot { private constructor(); /** diff --git a/src/components/CodeEditor/github-dark-default.json b/src/components/CodeEditor/github-dark-default.json new file mode 100644 index 00000000..c4dd4815 --- /dev/null +++ b/src/components/CodeEditor/github-dark-default.json @@ -0,0 +1,535 @@ +{ + "inherit": true, + "base": "vs-dark", + "colors": { + "focusBorder": "#1f6feb", + "foreground": "#c9d1d9", + "descriptionForeground": "#8b949e", + "errorForeground": "#f85149", + "textLink.foreground": "#58a6ff", + "textLink.activeForeground": "#58a6ff", + "textBlockQuote.background": "#010409", + "textBlockQuote.border": "#30363d", + "textCodeBlock.background": "#6e768166", + "textPreformat.foreground": "#8b949e", + "textSeparator.foreground": "#21262d", + "button.background": "#238636", + "button.foreground": "#ffffff", + "button.hoverBackground": "#2ea043", + "button.secondaryBackground": "#282e33", + "button.secondaryForeground": "#c9d1d9", + "button.secondaryHoverBackground": "#30363d", + "checkbox.background": "#161b22", + "checkbox.border": "#30363d", + "dropdown.background": "#161b22", + "dropdown.border": "#30363d", + "dropdown.foreground": "#c9d1d9", + "dropdown.listBackground": "#161b22", + "input.background": "#0d1117", + "input.border": "#30363d", + "input.foreground": "#c9d1d9", + "input.placeholderForeground": "#484f58", + "badge.foreground": "#f0f6fc", + "badge.background": "#1f6feb", + "progressBar.background": "#1f6feb", + "titleBar.activeForeground": "#8b949e", + "titleBar.activeBackground": "#0d1117", + "titleBar.inactiveForeground": "#8b949e", + "titleBar.inactiveBackground": "#010409", + "titleBar.border": "#30363d", + "activityBar.foreground": "#c9d1d9", + "activityBar.inactiveForeground": "#8b949e", + "activityBar.background": "#0d1117", + "activityBarBadge.foreground": "#f0f6fc", + "activityBarBadge.background": "#1f6feb", + "activityBar.activeBorder": "#f78166", + "activityBar.border": "#30363d", + "sideBar.foreground": "#c9d1d9", + "sideBar.background": "#010409", + "sideBar.border": "#30363d", + "sideBarTitle.foreground": "#c9d1d9", + "sideBarSectionHeader.foreground": "#c9d1d9", + "sideBarSectionHeader.background": "#010409", + "sideBarSectionHeader.border": "#30363d", + "list.hoverForeground": "#c9d1d9", + "list.inactiveSelectionForeground": "#c9d1d9", + "list.activeSelectionForeground": "#c9d1d9", + "list.hoverBackground": "#6e76811a", + "list.inactiveSelectionBackground": "#6e768166", + "list.activeSelectionBackground": "#6e768166", + "list.focusForeground": "#c9d1d9", + "list.focusBackground": "#388bfd26", + "list.inactiveFocusBackground": "#388bfd26", + "list.highlightForeground": "#58a6ff", + "tree.indentGuidesStroke": "#21262d", + "notificationCenterHeader.foreground": "#8b949e", + "notificationCenterHeader.background": "#161b22", + "notifications.foreground": "#c9d1d9", + "notifications.background": "#161b22", + "notifications.border": "#30363d", + "notificationsErrorIcon.foreground": "#f85149", + "notificationsWarningIcon.foreground": "#d29922", + "notificationsInfoIcon.foreground": "#58a6ff", + "pickerGroup.border": "#30363d", + "pickerGroup.foreground": "#8b949e", + "quickInput.background": "#161b22", + "quickInput.foreground": "#c9d1d9", + "statusBar.foreground": "#8b949e", + "statusBar.background": "#0d1117", + "statusBar.border": "#30363d", + "statusBar.noFolderBackground": "#0d1117", + "statusBar.debuggingBackground": "#da3633", + "statusBar.debuggingForeground": "#f0f6fc", + "statusBarItem.prominentBackground": "#161b22", + "editorGroupHeader.tabsBackground": "#010409", + "editorGroupHeader.tabsBorder": "#30363d", + "editorGroup.border": "#30363d", + "tab.activeForeground": "#c9d1d9", + "tab.inactiveForeground": "#8b949e", + "tab.inactiveBackground": "#010409", + "tab.activeBackground": "#0d1117", + "tab.hoverBackground": "#0d1117", + "tab.unfocusedHoverBackground": "#6e76811a", + "tab.border": "#30363d", + "tab.unfocusedActiveBorderTop": "#30363d", + "tab.activeBorder": "#0d1117", + "tab.unfocusedActiveBorder": "#0d1117", + "tab.activeBorderTop": "#f78166", + "breadcrumb.foreground": "#8b949e", + "breadcrumb.focusForeground": "#c9d1d9", + "breadcrumb.activeSelectionForeground": "#8b949e", + "breadcrumbPicker.background": "#161b22", + "editor.foreground": "#c9d1d9", + "editor.background": "#0d1117", + "editorWidget.background": "#161b22", + "editor.foldBackground": "#6e76811a", + "editor.lineHighlightBackground": "#6e76811a", + "editorLineNumber.foreground": "#8b949e", + "editorLineNumber.activeForeground": "#c9d1d9", + "editorIndentGuide.background": "#21262d", + "editorIndentGuide.activeBackground": "#30363d", + "editorWhitespace.foreground": "#484f58", + "editorCursor.foreground": "#58a6ff", + "editor.findMatchBackground": "#ffd33d44", + "editor.findMatchHighlightBackground": "#ffd33d22", + "editor.linkedEditingBackground": "#3392FF22", + "editor.inactiveSelectionBackground": "#3392FF22", + "editor.selectionBackground": "#3392FF44", + "editor.selectionHighlightBackground": "#17E5E633", + "editor.selectionHighlightBorder": "#17E5E600", + "editor.wordHighlightBackground": "#17E5E600", + "editor.wordHighlightStrongBackground": "#17E5E600", + "editor.wordHighlightBorder": "#17E5E699", + "editor.wordHighlightStrongBorder": "#17E5E666", + "editorBracketMatch.background": "#17E5E650", + "editorBracketMatch.border": "#17E5E600", + "editorGutter.modifiedBackground": "#bb800966", + "editorGutter.addedBackground": "#2ea04366", + "editorGutter.deletedBackground": "#f8514966", + "diffEditor.insertedTextBackground": "#2ea04326", + "diffEditor.removedTextBackground": "#f8514926", + "scrollbar.shadow": "#0008", + "scrollbarSlider.background": "#484F5833", + "scrollbarSlider.hoverBackground": "#484F5844", + "scrollbarSlider.activeBackground": "#484F5888", + "editorOverviewRuler.border": "#010409", + "panel.background": "#010409", + "panel.border": "#30363d", + "panelTitle.activeBorder": "#f78166", + "panelTitle.activeForeground": "#c9d1d9", + "panelTitle.inactiveForeground": "#8b949e", + "panelInput.border": "#30363d", + "terminal.foreground": "#8b949e", + "terminal.ansiBlack": "#484f58", + "terminal.ansiRed": "#ff7b72", + "terminal.ansiGreen": "#3fb950", + "terminal.ansiYellow": "#d29922", + "terminal.ansiBlue": "#58a6ff", + "terminal.ansiMagenta": "#bc8cff", + "terminal.ansiCyan": "#39c5cf", + "terminal.ansiWhite": "#b1bac4", + "terminal.ansiBrightBlack": "#6e7681", + "terminal.ansiBrightRed": "#ffa198", + "terminal.ansiBrightGreen": "#56d364", + "terminal.ansiBrightYellow": "#e3b341", + "terminal.ansiBrightBlue": "#79c0ff", + "terminal.ansiBrightMagenta": "#d2a8ff", + "terminal.ansiBrightCyan": "#56d4dd", + "terminal.ansiBrightWhite": "#f0f6fc", + "gitDecoration.addedResourceForeground": "#3fb950", + "gitDecoration.modifiedResourceForeground": "#d29922", + "gitDecoration.deletedResourceForeground": "#f85149", + "gitDecoration.untrackedResourceForeground": "#3fb950", + "gitDecoration.ignoredResourceForeground": "#484f58", + "gitDecoration.conflictingResourceForeground": "#db6d28", + "gitDecoration.submoduleResourceForeground": "#8b949e", + "debugToolBar.background": "#161b22", + "editor.stackFrameHighlightBackground": "#D2992225", + "editor.focusedStackFrameHighlightBackground": "#3FB95025", + "peekViewEditor.matchHighlightBackground": "#ffd33d33", + "peekViewResult.matchHighlightBackground": "#ffd33d33", + "peekViewEditor.background": "#0d111788", + "peekViewResult.background": "#0d1117", + "settings.headerForeground": "#8b949e", + "settings.modifiedItemIndicator": "#bb800966", + "welcomePage.buttonBackground": "#21262d", + "welcomePage.buttonHoverBackground": "#30363d" + }, + "rules": [ + { + "foreground": "#8b949e", + "token": "comment" + }, + { + "foreground": "#8b949e", + "token": "punctuation.definition.comment" + }, + { + "foreground": "#8b949e", + "token": "string.comment" + }, + { + "foreground": "#79c0ff", + "token": "constant" + }, + { + "foreground": "#79c0ff", + "token": "entity.name.constant" + }, + { + "foreground": "#79c0ff", + "token": "variable.other.constant" + }, + { + "foreground": "#79c0ff", + "token": "variable.language" + }, + { + "foreground": "#79c0ff", + "token": "entity" + }, + { + "foreground": "#ffa657", + "token": "entity.name" + }, + { + "foreground": "#ffa657", + "token": "meta.export.default" + }, + { + "foreground": "#ffa657", + "token": "meta.definition.variable" + }, + { + "foreground": "#c9d1d9", + "token": "variable.parameter.function" + }, + { + "foreground": "#c9d1d9", + "token": "meta.jsx.children" + }, + { + "foreground": "#c9d1d9", + "token": "meta.block" + }, + { + "foreground": "#c9d1d9", + "token": "meta.tag.attributes" + }, + { + "foreground": "#c9d1d9", + "token": "entity.name.constant" + }, + { + "foreground": "#c9d1d9", + "token": "meta.object.member" + }, + { + "foreground": "#c9d1d9", + "token": "meta.embedded.expression" + }, + { + "foreground": "#d2a8ff", + "token": "entity.name.function" + }, + { + "foreground": "#7ee787", + "token": "entity.name.tag" + }, + { + "foreground": "#7ee787", + "token": "support.class.component" + }, + { + "foreground": "#ff7b72", + "token": "keyword" + }, + { + "foreground": "#ff7b72", + "token": "storage" + }, + { + "foreground": "#ff7b72", + "token": "storage.type" + }, + { + "foreground": "#c9d1d9", + "token": "storage.modifier.package" + }, + { + "foreground": "#c9d1d9", + "token": "storage.modifier.import" + }, + { + "foreground": "#c9d1d9", + "token": "storage.type.java" + }, + { + "foreground": "#a5d6ff", + "token": "string" + }, + { + "foreground": "#a5d6ff", + "token": "punctuation.definition.string" + }, + { + "foreground": "#a5d6ff", + "token": "string punctuation.section.embedded source" + }, + { + "foreground": "#79c0ff", + "token": "support" + }, + { + "foreground": "#79c0ff", + "token": "meta.property-name" + }, + { + "foreground": "#ffa657", + "token": "variable" + }, + { + "foreground": "#c9d1d9", + "token": "variable.other" + }, + { + "fontStyle": "italic", + "foreground": "#ffa198", + "token": "invalid.broken" + }, + { + "fontStyle": "italic", + "foreground": "#ffa198", + "token": "invalid.deprecated" + }, + { + "fontStyle": "italic", + "foreground": "#ffa198", + "token": "invalid.illegal" + }, + { + "fontStyle": "italic", + "foreground": "#ffa198", + "token": "invalid.unimplemented" + }, + { + "fontStyle": "italic underline", + "background": "#ff7b72", + "foreground": "#0d1117", + "content": "^M", + "token": "carriage-return" + }, + { + "foreground": "#ffa198", + "token": "message.error" + }, + { + "foreground": "#c9d1d9", + "token": "string source" + }, + { + "foreground": "#79c0ff", + "token": "string variable" + }, + { + "foreground": "#a5d6ff", + "token": "source.regexp" + }, + { + "foreground": "#a5d6ff", + "token": "string.regexp" + }, + { + "foreground": "#a5d6ff", + "token": "string.regexp.character-class" + }, + { + "foreground": "#a5d6ff", + "token": "string.regexp constant.character.escape" + }, + { + "foreground": "#a5d6ff", + "token": "string.regexp source.ruby.embedded" + }, + { + "foreground": "#a5d6ff", + "token": "string.regexp string.regexp.arbitrary-repitition" + }, + { + "fontStyle": "bold", + "foreground": "#7ee787", + "token": "string.regexp constant.character.escape" + }, + { + "foreground": "#79c0ff", + "token": "support.constant" + }, + { + "foreground": "#79c0ff", + "token": "support.variable" + }, + { + "foreground": "#79c0ff", + "token": "meta.module-reference" + }, + { + "foreground": "#ffa657", + "token": "punctuation.definition.list.begin.markdown" + }, + { + "fontStyle": "bold", + "foreground": "#79c0ff", + "token": "markup.heading" + }, + { + "fontStyle": "bold", + "foreground": "#79c0ff", + "token": "markup.heading entity.name" + }, + { + "foreground": "#7ee787", + "token": "markup.quote" + }, + { + "fontStyle": "italic", + "foreground": "#c9d1d9", + "token": "markup.italic" + }, + { + "fontStyle": "bold", + "foreground": "#c9d1d9", + "token": "markup.bold" + }, + { + "foreground": "#79c0ff", + "token": "markup.raw" + }, + { + "background": "#490202", + "foreground": "#ffa198", + "token": "markup.deleted" + }, + { + "background": "#490202", + "foreground": "#ffa198", + "token": "meta.diff.header.from-file" + }, + { + "background": "#490202", + "foreground": "#ffa198", + "token": "punctuation.definition.deleted" + }, + { + "background": "#04260f", + "foreground": "#7ee787", + "token": "markup.inserted" + }, + { + "background": "#04260f", + "foreground": "#7ee787", + "token": "meta.diff.header.to-file" + }, + { + "background": "#04260f", + "foreground": "#7ee787", + "token": "punctuation.definition.inserted" + }, + { + "background": "#5a1e02", + "foreground": "#ffa657", + "token": "markup.changed" + }, + { + "background": "#5a1e02", + "foreground": "#ffa657", + "token": "punctuation.definition.changed" + }, + { + "foreground": "#161b22", + "background": "#79c0ff", + "token": "markup.ignored" + }, + { + "foreground": "#161b22", + "background": "#79c0ff", + "token": "markup.untracked" + }, + { + "foreground": "#d2a8ff", + "fontStyle": "bold", + "token": "meta.diff.range" + }, + { + "foreground": "#79c0ff", + "token": "meta.diff.header" + }, + { + "fontStyle": "bold", + "foreground": "#79c0ff", + "token": "meta.separator" + }, + { + "foreground": "#79c0ff", + "token": "meta.output" + }, + { + "foreground": "#8b949e", + "token": "brackethighlighter.tag" + }, + { + "foreground": "#8b949e", + "token": "brackethighlighter.curly" + }, + { + "foreground": "#8b949e", + "token": "brackethighlighter.round" + }, + { + "foreground": "#8b949e", + "token": "brackethighlighter.square" + }, + { + "foreground": "#8b949e", + "token": "brackethighlighter.angle" + }, + { + "foreground": "#8b949e", + "token": "brackethighlighter.quote" + }, + { + "foreground": "#ffa198", + "token": "brackethighlighter.unmatched" + }, + { + "foreground": "#a5d6ff", + "fontStyle": "underline", + "token": "constant.other.reference.link" + }, + { + "foreground": "#a5d6ff", + "fontStyle": "underline", + "token": "string.other.link" + } + ], + "encodedTokensColors": [] +} diff --git a/src/components/CodeEditor/github-light-default.json b/src/components/CodeEditor/github-light-default.json new file mode 100644 index 00000000..da31839d --- /dev/null +++ b/src/components/CodeEditor/github-light-default.json @@ -0,0 +1,531 @@ +{ + "inherit": true, + "base": "vs", + "colors": { + "focusBorder": "#0969da", + "foreground": "#24292f", + "descriptionForeground": "#57606a", + "errorForeground": "#cf222e", + "textLink.foreground": "#0969da", + "textLink.activeForeground": "#0969da", + "textBlockQuote.background": "#f6f8fa", + "textBlockQuote.border": "#d0d7de", + "textCodeBlock.background": "#afb8c133", + "textPreformat.foreground": "#57606a", + "textSeparator.foreground": "#d8dee4", + "button.background": "#2da44e", + "button.foreground": "#ffffff", + "button.hoverBackground": "#2c974b", + "button.secondaryBackground": "#ebecf0", + "button.secondaryForeground": "#24292f", + "button.secondaryHoverBackground": "#f3f4f6", + "checkbox.background": "#f6f8fa", + "checkbox.border": "#d0d7de", + "dropdown.background": "#ffffff", + "dropdown.border": "#d0d7de", + "dropdown.foreground": "#24292f", + "dropdown.listBackground": "#ffffff", + "input.background": "#ffffff", + "input.border": "#d0d7de", + "input.foreground": "#24292f", + "input.placeholderForeground": "#6e7781", + "badge.foreground": "#ffffff", + "badge.background": "#0969da", + "progressBar.background": "#0969da", + "titleBar.activeForeground": "#57606a", + "titleBar.activeBackground": "#ffffff", + "titleBar.inactiveForeground": "#57606a", + "titleBar.inactiveBackground": "#f6f8fa", + "titleBar.border": "#d0d7de", + "activityBar.foreground": "#24292f", + "activityBar.inactiveForeground": "#57606a", + "activityBar.background": "#ffffff", + "activityBarBadge.foreground": "#ffffff", + "activityBarBadge.background": "#0969da", + "activityBar.activeBorder": "#fd8c73", + "activityBar.border": "#d0d7de", + "sideBar.foreground": "#24292f", + "sideBar.background": "#f6f8fa", + "sideBar.border": "#d0d7de", + "sideBarTitle.foreground": "#24292f", + "sideBarSectionHeader.foreground": "#24292f", + "sideBarSectionHeader.background": "#f6f8fa", + "sideBarSectionHeader.border": "#d0d7de", + "list.hoverForeground": "#24292f", + "list.inactiveSelectionForeground": "#24292f", + "list.activeSelectionForeground": "#24292f", + "list.hoverBackground": "#eaeef280", + "list.inactiveSelectionBackground": "#afb8c133", + "list.activeSelectionBackground": "#afb8c133", + "list.focusForeground": "#24292f", + "list.focusBackground": "#ddf4ff", + "list.inactiveFocusBackground": "#ddf4ff", + "list.highlightForeground": "#0969da", + "tree.indentGuidesStroke": "#d8dee4", + "notificationCenterHeader.foreground": "#57606a", + "notificationCenterHeader.background": "#f6f8fa", + "notifications.foreground": "#24292f", + "notifications.background": "#ffffff", + "notifications.border": "#d0d7de", + "notificationsErrorIcon.foreground": "#cf222e", + "notificationsWarningIcon.foreground": "#9a6700", + "notificationsInfoIcon.foreground": "#0969da", + "pickerGroup.border": "#d0d7de", + "pickerGroup.foreground": "#57606a", + "quickInput.background": "#ffffff", + "quickInput.foreground": "#24292f", + "statusBar.foreground": "#57606a", + "statusBar.background": "#ffffff", + "statusBar.border": "#d0d7de", + "statusBar.noFolderBackground": "#ffffff", + "statusBar.debuggingBackground": "#cf222e", + "statusBar.debuggingForeground": "#ffffff", + "statusBarItem.prominentBackground": "#f6f8fa", + "editorGroupHeader.tabsBackground": "#f6f8fa", + "editorGroupHeader.tabsBorder": "#d0d7de", + "editorGroup.border": "#d0d7de", + "tab.activeForeground": "#24292f", + "tab.inactiveForeground": "#57606a", + "tab.inactiveBackground": "#f6f8fa", + "tab.activeBackground": "#ffffff", + "tab.hoverBackground": "#ffffff", + "tab.unfocusedHoverBackground": "#eaeef280", + "tab.border": "#d0d7de", + "tab.unfocusedActiveBorderTop": "#d0d7de", + "tab.activeBorder": "#ffffff", + "tab.unfocusedActiveBorder": "#ffffff", + "tab.activeBorderTop": "#fd8c73", + "breadcrumb.foreground": "#57606a", + "breadcrumb.focusForeground": "#24292f", + "breadcrumb.activeSelectionForeground": "#57606a", + "breadcrumbPicker.background": "#ffffff", + "editor.foreground": "#24292f", + "editor.background": "#ffffff", + "editorWidget.background": "#ffffff", + "editor.foldBackground": "#6e77811a", + "editor.lineHighlightBackground": "#eaeef280", + "editorLineNumber.foreground": "#57606a", + "editorLineNumber.activeForeground": "#24292f", + "editorIndentGuide.background": "#d8dee4", + "editorIndentGuide.activeBackground": "#d0d7de", + "editorWhitespace.foreground": "#6e7781", + "editorCursor.foreground": "#0969da", + "editor.findMatchBackground": "#bf8700", + "editor.findMatchHighlightBackground": "#ffdf5d66", + "editor.linkedEditingBackground": "#0366d611", + "editor.inactiveSelectionBackground": "#0366d611", + "editor.selectionBackground": "#0366d625", + "editor.selectionHighlightBackground": "#34d05840", + "editor.selectionHighlightBorder": "#34d05800", + "editor.wordHighlightBackground": "#34d05800", + "editor.wordHighlightStrongBackground": "#34d05800", + "editor.wordHighlightBorder": "#24943e99", + "editor.wordHighlightStrongBorder": "#24943e50", + "editorBracketMatch.background": "#34d05840", + "editorBracketMatch.border": "#34d05800", + "editorGutter.modifiedBackground": "#d4a72c66", + "editorGutter.addedBackground": "#4ac26b66", + "editorGutter.deletedBackground": "#ff818266", + "diffEditor.insertedTextBackground": "#dafbe1", + "diffEditor.removedTextBackground": "#ffebe9", + "scrollbar.shadow": "#6a737d33", + "scrollbarSlider.background": "#959da533", + "scrollbarSlider.hoverBackground": "#959da544", + "scrollbarSlider.activeBackground": "#959da588", + "editorOverviewRuler.border": "#ffffff", + "panel.background": "#f6f8fa", + "panel.border": "#d0d7de", + "panelTitle.activeBorder": "#fd8c73", + "panelTitle.activeForeground": "#24292f", + "panelTitle.inactiveForeground": "#57606a", + "panelInput.border": "#d0d7de", + "terminal.foreground": "#57606a", + "terminal.ansiBlack": "#24292f", + "terminal.ansiRed": "#cf222e", + "terminal.ansiGreen": "#116329", + "terminal.ansiYellow": "#4d2d00", + "terminal.ansiBlue": "#0969da", + "terminal.ansiMagenta": "#8250df", + "terminal.ansiCyan": "#1b7c83", + "terminal.ansiWhite": "#6e7781", + "terminal.ansiBrightBlack": "#57606a", + "terminal.ansiBrightRed": "#a40e26", + "terminal.ansiBrightGreen": "#1a7f37", + "terminal.ansiBrightYellow": "#633c01", + "terminal.ansiBrightBlue": "#218bff", + "terminal.ansiBrightMagenta": "#a475f9", + "terminal.ansiBrightCyan": "#3192aa", + "terminal.ansiBrightWhite": "#8c959f", + "gitDecoration.addedResourceForeground": "#1a7f37", + "gitDecoration.modifiedResourceForeground": "#9a6700", + "gitDecoration.deletedResourceForeground": "#cf222e", + "gitDecoration.untrackedResourceForeground": "#1a7f37", + "gitDecoration.ignoredResourceForeground": "#6e7781", + "gitDecoration.conflictingResourceForeground": "#bc4c00", + "gitDecoration.submoduleResourceForeground": "#57606a", + "debugToolBar.background": "#ffffff", + "editor.stackFrameHighlightBackground": "#ffd33d33", + "editor.focusedStackFrameHighlightBackground": "#28a74525", + "settings.headerForeground": "#57606a", + "settings.modifiedItemIndicator": "#d4a72c66", + "welcomePage.buttonBackground": "#f6f8fa", + "welcomePage.buttonHoverBackground": "#f3f4f6" + }, + "rules": [ + { + "foreground": "#6e7781", + "token": "comment" + }, + { + "foreground": "#6e7781", + "token": "punctuation.definition.comment" + }, + { + "foreground": "#6e7781", + "token": "string.comment" + }, + { + "foreground": "#0550ae", + "token": "constant" + }, + { + "foreground": "#0550ae", + "token": "entity.name.constant" + }, + { + "foreground": "#0550ae", + "token": "variable.other.constant" + }, + { + "foreground": "#0550ae", + "token": "variable.language" + }, + { + "foreground": "#0550ae", + "token": "entity" + }, + { + "foreground": "#953800", + "token": "entity.name" + }, + { + "foreground": "#953800", + "token": "meta.export.default" + }, + { + "foreground": "#953800", + "token": "meta.definition.variable" + }, + { + "foreground": "#24292f", + "token": "variable.parameter.function" + }, + { + "foreground": "#24292f", + "token": "meta.jsx.children" + }, + { + "foreground": "#24292f", + "token": "meta.block" + }, + { + "foreground": "#24292f", + "token": "meta.tag.attributes" + }, + { + "foreground": "#24292f", + "token": "entity.name.constant" + }, + { + "foreground": "#24292f", + "token": "meta.object.member" + }, + { + "foreground": "#24292f", + "token": "meta.embedded.expression" + }, + { + "foreground": "#8250df", + "token": "entity.name.function" + }, + { + "foreground": "#116329", + "token": "entity.name.tag" + }, + { + "foreground": "#116329", + "token": "support.class.component" + }, + { + "foreground": "#cf222e", + "token": "keyword" + }, + { + "foreground": "#cf222e", + "token": "storage" + }, + { + "foreground": "#cf222e", + "token": "storage.type" + }, + { + "foreground": "#24292f", + "token": "storage.modifier.package" + }, + { + "foreground": "#24292f", + "token": "storage.modifier.import" + }, + { + "foreground": "#24292f", + "token": "storage.type.java" + }, + { + "foreground": "#0a3069", + "token": "string" + }, + { + "foreground": "#0a3069", + "token": "punctuation.definition.string" + }, + { + "foreground": "#0a3069", + "token": "string punctuation.section.embedded source" + }, + { + "foreground": "#0550ae", + "token": "support" + }, + { + "foreground": "#0550ae", + "token": "meta.property-name" + }, + { + "foreground": "#953800", + "token": "variable" + }, + { + "foreground": "#24292f", + "token": "variable.other" + }, + { + "fontStyle": "italic", + "foreground": "#82071e", + "token": "invalid.broken" + }, + { + "fontStyle": "italic", + "foreground": "#82071e", + "token": "invalid.deprecated" + }, + { + "fontStyle": "italic", + "foreground": "#82071e", + "token": "invalid.illegal" + }, + { + "fontStyle": "italic", + "foreground": "#82071e", + "token": "invalid.unimplemented" + }, + { + "fontStyle": "italic underline", + "background": "#cf222e", + "foreground": "#f6f8fa", + "content": "^M", + "token": "carriage-return" + }, + { + "foreground": "#82071e", + "token": "message.error" + }, + { + "foreground": "#24292f", + "token": "string source" + }, + { + "foreground": "#0550ae", + "token": "string variable" + }, + { + "foreground": "#0a3069", + "token": "source.regexp" + }, + { + "foreground": "#0a3069", + "token": "string.regexp" + }, + { + "foreground": "#0a3069", + "token": "string.regexp.character-class" + }, + { + "foreground": "#0a3069", + "token": "string.regexp constant.character.escape" + }, + { + "foreground": "#0a3069", + "token": "string.regexp source.ruby.embedded" + }, + { + "foreground": "#0a3069", + "token": "string.regexp string.regexp.arbitrary-repitition" + }, + { + "fontStyle": "bold", + "foreground": "#116329", + "token": "string.regexp constant.character.escape" + }, + { + "foreground": "#0550ae", + "token": "support.constant" + }, + { + "foreground": "#0550ae", + "token": "support.variable" + }, + { + "foreground": "#0550ae", + "token": "meta.module-reference" + }, + { + "foreground": "#953800", + "token": "punctuation.definition.list.begin.markdown" + }, + { + "fontStyle": "bold", + "foreground": "#0550ae", + "token": "markup.heading" + }, + { + "fontStyle": "bold", + "foreground": "#0550ae", + "token": "markup.heading entity.name" + }, + { + "foreground": "#116329", + "token": "markup.quote" + }, + { + "fontStyle": "italic", + "foreground": "#24292f", + "token": "markup.italic" + }, + { + "fontStyle": "bold", + "foreground": "#24292f", + "token": "markup.bold" + }, + { + "foreground": "#0550ae", + "token": "markup.raw" + }, + { + "background": "#FFEBE9", + "foreground": "#82071e", + "token": "markup.deleted" + }, + { + "background": "#FFEBE9", + "foreground": "#82071e", + "token": "meta.diff.header.from-file" + }, + { + "background": "#FFEBE9", + "foreground": "#82071e", + "token": "punctuation.definition.deleted" + }, + { + "background": "#dafbe1", + "foreground": "#116329", + "token": "markup.inserted" + }, + { + "background": "#dafbe1", + "foreground": "#116329", + "token": "meta.diff.header.to-file" + }, + { + "background": "#dafbe1", + "foreground": "#116329", + "token": "punctuation.definition.inserted" + }, + { + "background": "#ffd8b5", + "foreground": "#953800", + "token": "markup.changed" + }, + { + "background": "#ffd8b5", + "foreground": "#953800", + "token": "punctuation.definition.changed" + }, + { + "foreground": "#eaeef2", + "background": "#0550ae", + "token": "markup.ignored" + }, + { + "foreground": "#eaeef2", + "background": "#0550ae", + "token": "markup.untracked" + }, + { + "foreground": "#8250df", + "fontStyle": "bold", + "token": "meta.diff.range" + }, + { + "foreground": "#0550ae", + "token": "meta.diff.header" + }, + { + "fontStyle": "bold", + "foreground": "#0550ae", + "token": "meta.separator" + }, + { + "foreground": "#0550ae", + "token": "meta.output" + }, + { + "foreground": "#57606a", + "token": "brackethighlighter.tag" + }, + { + "foreground": "#57606a", + "token": "brackethighlighter.curly" + }, + { + "foreground": "#57606a", + "token": "brackethighlighter.round" + }, + { + "foreground": "#57606a", + "token": "brackethighlighter.square" + }, + { + "foreground": "#57606a", + "token": "brackethighlighter.angle" + }, + { + "foreground": "#57606a", + "token": "brackethighlighter.quote" + }, + { + "foreground": "#82071e", + "token": "brackethighlighter.unmatched" + }, + { + "foreground": "#0a3069", + "fontStyle": "underline", + "token": "constant.other.reference.link" + }, + { + "foreground": "#0a3069", + "fontStyle": "underline", + "token": "string.other.link" + } + ], + "encodedTokensColors": [] +} diff --git a/src/components/CodeEditor/index.tsx b/src/components/CodeEditor/index.tsx new file mode 100644 index 00000000..3e8596c3 --- /dev/null +++ b/src/components/CodeEditor/index.tsx @@ -0,0 +1,232 @@ +import { useState, useEffect } from "react"; + +import Editor, { EditorProps, useMonaco } from "@monaco-editor/react"; +import type { languages } from "monaco-editor/esm/vs/editor/editor.api"; +import githubLightTheme from "./github-light-default.json"; +import githubDarkTheme from "./github-dark-default.json"; + +import { useTheme, Box, BoxProps } from "@mui/material"; +import CircularProgressOptical from "components/CircularProgressOptical"; +import ResizeBottomRightIcon from "assets/icons/ResizeBottomRight"; + +import { useProjectContext } from "contexts/ProjectContext"; +import { getFieldProp } from "components/fields"; + +/* eslint-disable import/no-webpack-loader-syntax */ +import firestoreDefs from "!!raw-loader!./firestore.d.ts"; +import firebaseAuthDefs from "!!raw-loader!./firebaseAuth.d.ts"; +import firebaseStorageDefs from "!!raw-loader!./firebaseStorage.d.ts"; +import utilsDefs from "!!raw-loader!./utils.d.ts"; +import extensionsDefs from "!!raw-loader!./extensions.d.ts"; + +export interface ICodeEditorProps extends Partial { + onChange: EditorProps["onChange"]; + value: string; + minHeight?: number; + disabled?: boolean; + containerProps?: Partial; + + extraLibs?: string[]; + onValidate?: EditorProps["onValidate"]; + onValidStatusUpdate?: ({ isValid: boolean }) => void; + diagnosticsOptions?: languages.typescript.DiagnosticsOptions; + onUnmount?: () => void; +} + +export default function CodeEditor({ + onChange, + value, + minHeight = 100, + disabled, + containerProps, + + extraLibs, + onValidate, + onValidStatusUpdate, + diagnosticsOptions, + onUnmount, + + ...props +}: ICodeEditorProps) { + const theme = useTheme(); + const { tableState } = useProjectContext(); + + const [initialEditorValue] = useState(value ?? ""); + const monaco = useMonaco(); + + useEffect(() => { + return () => { + onUnmount?.(); + }; + }, []); + + const onValidate_: EditorProps["onValidate"] = (markers) => { + if (onValidStatusUpdate) + onValidStatusUpdate({ isValid: markers.length <= 0 }); + else if (onValidate) onValidate(markers); + }; + + useEffect(() => { + if (!monaco) { + // useMonaco returns a monaco instance but initialisation is done asynchronously + // dont execute the logic until the instance is initialised + return; + } + + setTimeout(() => { + try { + monaco.editor.defineTheme("github-light", githubLightTheme as any); + monaco.editor.defineTheme("github-dark", githubDarkTheme as any); + monaco.editor.setTheme("github-" + theme.palette.mode); + } catch (error) { + console.error("Could not set Monaco theme: ", error); + } + }); + }, [monaco, theme.palette.mode]); + + useEffect(() => { + if (!monaco) { + // useMonaco returns a monaco instance but initialisation is done asynchronously + // dont execute the logic until the instance is initialised + return; + } + + try { + monaco.editor.defineTheme("github-light", githubLightTheme as any); + monaco.editor.defineTheme("github-dark", githubDarkTheme as any); + + monaco.languages.typescript.javascriptDefaults.addExtraLib(firestoreDefs); + monaco.languages.typescript.javascriptDefaults.addExtraLib( + firebaseAuthDefs + ); + monaco.languages.typescript.javascriptDefaults.addExtraLib( + firebaseStorageDefs + ); + monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions( + diagnosticsOptions ?? { + noSemanticValidation: true, + noSyntaxValidation: false, + } + ); + // compiler options + monaco.languages.typescript.javascriptDefaults.setCompilerOptions({ + target: monaco.languages.typescript.ScriptTarget.ES2020, + allowNonTsExtensions: true, + }); + if (extraLibs) { + monaco.languages.typescript.javascriptDefaults.addExtraLib( + extraLibs.join("\n"), + "ts:filename/extraLibs.d.ts" + ); + } + monaco.languages.typescript.javascriptDefaults.addExtraLib( + utilsDefs, + "ts:filename/utils.d.ts" + ); + + const rowDefinition = + Object.keys(tableState?.columns!) + .map((columnKey: string) => { + const column = tableState?.columns[columnKey]; + return `static ${columnKey}: ${getFieldProp("type", column.type)}`; + }) + .join(";\n") + ";"; + + const availableFields = Object.keys(tableState?.columns!) + .map((columnKey: string) => `"${columnKey}"`) + .join("|\n"); + + monaco.languages.typescript.javascriptDefaults.addExtraLib( + [ + "/**", + " * extensions type configuration", + " */", + "// basic types that are used in all places", + `type Row = {${rowDefinition}};`, + `type Field = ${availableFields} | string | object;`, + `type Fields = Field[];`, + extensionsDefs, + ].join("\n"), + "ts:filename/extensions.d.ts" + ); + + monaco.languages.typescript.javascriptDefaults.addExtraLib( + [ + "declare var require: any;", + "declare var Buffer: any;", + "const ref: FirebaseFirestore.DocumentReference;", + "const storage: firebasestorage.Storage;", + "const db: FirebaseFirestore.Firestore;", + "const auth: adminauth.BaseAuth;", + "declare class row {", + " /**", + " * Returns the row fields", + " */", + rowDefinition, + "}", + ].join("\n"), + "ts:filename/rowFields.d.ts" + ); + } catch (error) { + console.error( + "An error occurred during initialization of Monaco: ", + error + ); + } + }, [tableState?.columns, monaco, diagnosticsOptions, extraLibs]); + + return ( + + } + className="editor" + {...props} + options={{ + readOnly: disabled, + fontFamily: theme.typography.fontFamilyMono, + rulers: [80], + minimap: { enabled: false }, + lineNumbersMinChars: 4, + lineDecorationsWidth: 0, + automaticLayout: true, + fixedOverflowWidgets: true, + ...props.options, + }} + /> + + + + ); +} diff --git a/src/components/CodeEditor/utils.d.ts b/src/components/CodeEditor/utils.d.ts new file mode 100644 index 00000000..38a02852 --- /dev/null +++ b/src/components/CodeEditor/utils.d.ts @@ -0,0 +1,50 @@ +/** + * utility functions + */ +declare namespace utilFns { + /** + * Sends out an email through sendGrid + */ + function sendEmail(msg: { + from: string; + templateId: string; + personalizations: { to: string; dynamic_template_data: any }[]; + }): void {} + + /** + * Gets the secret defined in Google Cloud Secret + */ + async function getSecret(name: string, v?: string): any {} + + /** + * Async version of forEach + */ + async function asyncForEach(array: any[], callback: Function): void {} + + /** + * Generate random ID from numbers and English characters including lowercase and uppercase + */ + function generateId(): string {} + + /** + * Add an item to an array field + */ + function arrayUnion(val: string): void {} + + /** + * Remove an item to an array field + */ + function arrayRemove(val: string): void {} + + /** + * Increment a number field + */ + function increment(val: number): void {} + + function hasRequiredFields(requiredFields: string[], data: any): boolean {} + + function hasAnyRole( + authorizedRoles: string[], + context: functions.https.CallableContext + ): boolean {} +} diff --git a/src/components/Table/ColumnMenu/FieldSettings/DefaultValueInput.tsx b/src/components/Table/ColumnMenu/FieldSettings/DefaultValueInput.tsx index 05b45960..93a65a3d 100644 --- a/src/components/Table/ColumnMenu/FieldSettings/DefaultValueInput.tsx +++ b/src/components/Table/ColumnMenu/FieldSettings/DefaultValueInput.tsx @@ -1,35 +1,21 @@ -import React from "react"; +import { lazy, Suspense, createElement } from "react"; import { useForm } from "react-hook-form"; import { IMenuModalProps } from ".."; -import { makeStyles, createStyles } from "@mui/styles"; import Checkbox from "@mui/material/Checkbox"; import FormControlLabel from "@mui/material/FormControlLabel"; import { Typography, TextField, MenuItem, ListItemText } from "@mui/material"; -import Subheading from "../Subheading"; import { getFieldProp } from "components/fields"; -import CodeEditorHelper from "components/CodeEditorHelper"; -import CodeEditor from "components/Table/editors/CodeEditor"; +import FieldSkeleton from "components/SideDrawer/Form/FieldSkeleton"; +import CodeEditorHelper from "@src/components/CodeEditor/CodeEditorHelper"; import FormAutosave from "./FormAutosave"; import { FieldType } from "constants/fields"; import { WIKI_LINKS } from "constants/externalLinks"; import { name } from "@root/package.json"; -const useStyles = makeStyles((theme) => - createStyles({ - typeSelect: { marginBottom: theme.spacing(1) }, - typeSelectItem: { whiteSpace: "normal" }, - - codeEditorContainer: { - border: `1px solid ${theme.palette.divider}`, - borderRadius: theme.shape.borderRadius, - }, - - mono: { - fontFamily: theme.typography.fontFamilyMono, - }, - }) +const CodeEditor = lazy( + () => import("components/CodeEditor" /* webpackChunkName: "CodeEditor" */) ); export interface IDefaultValueInputProps extends IMenuModalProps { @@ -43,7 +29,6 @@ export default function DefaultValueInput({ fieldName, ...props }: IDefaultValueInputProps) { - const classes = useStyles(); const _type = type !== FieldType.derivative ? type @@ -64,13 +49,17 @@ export default function DefaultValueInput({ value={config.defaultValue?.type ?? "undefined"} onChange={(e) => handleChange("defaultValue.type")(e.target.value)} fullWidth - className={classes.typeSelect} + sx={{ mb: 1 }} + SelectProps={{ + MenuProps: { + sx: { "& .MuiListItemText-root": { whiteSpace: "normal" } }, + }, + }} > @@ -78,24 +67,21 @@ export default function DefaultValueInput({ primary="Null" secondary={ <> - Initialise as null. + Initialise as null. } - className={classes.typeSelectItem} /> @@ -135,7 +121,7 @@ export default function DefaultValueInput({ } /> - {React.createElement(customFieldInput, { + {createElement(customFieldInput, { column: { type, key: fieldName, config, ...props, ...config }, control, docRef: {}, @@ -147,18 +133,12 @@ export default function DefaultValueInput({ {config.defaultValue?.type === "dynamic" && ( <> -
+ }> -
+ )} diff --git a/src/components/Table/Settings/Webhooks.tsx b/src/components/Table/Settings/Webhooks.tsx index bb00aa4e..ad17cb9c 100644 --- a/src/components/Table/Settings/Webhooks.tsx +++ b/src/components/Table/Settings/Webhooks.tsx @@ -15,7 +15,7 @@ import InputLabel from "@mui/material/InputLabel"; import MenuItem from "@mui/material/MenuItem"; import Select from "@mui/material/Select"; import Switch from "@mui/material/Switch"; -import CodeEditor from "../editors/CodeEditor"; +// import CodeEditor from "../editors/CodeEditor"; import { useProjectContext } from "contexts/ProjectContext"; import { makeId } from "../../../utils/fns"; @@ -144,12 +144,12 @@ export default function WebhooksDialog({ open, handleClose }) { Custom - {state.type === WebhookTypes.custom && ( + {/* {state.type === WebhookTypes.custom && ( - )} + )} */}
{state.type === WebhookTypes.typeForm && ( <> diff --git a/src/components/Table/TableHeader/Extensions/Step3Conditions.tsx b/src/components/Table/TableHeader/Extensions/Step3Conditions.tsx index ad23b46b..f5df76c4 100644 --- a/src/components/Table/TableHeader/Extensions/Step3Conditions.tsx +++ b/src/components/Table/TableHeader/Extensions/Step3Conditions.tsx @@ -1,11 +1,15 @@ +import { lazy, Suspense } from "react"; import { IExtensionModalStepProps } from "./ExtensionModal"; import useStateRef from "react-usestateref"; -import CodeEditor from "components/Table/editors/CodeEditor"; -import CodeEditorHelper from "components/CodeEditorHelper"; - +import FieldSkeleton from "components/SideDrawer/Form/FieldSkeleton"; +import CodeEditorHelper from "@src/components/CodeEditor/CodeEditorHelper"; import { WIKI_LINKS } from "constants/externalLinks"; +const CodeEditor = lazy( + () => import("components/CodeEditor" /* webpackChunkName: "CodeEditor" */) +); + const additionalVariables = [ { key: "change", @@ -38,14 +42,14 @@ export default function Step3Conditions({ return ( <> -
+ }> { + value={extensionObject.conditions} + minHeight={200} + onChange={(newValue) => { setExtensionObject({ ...extensionObject, - conditions: newValue, + conditions: newValue || "", }); }} onValidStatusUpdate={({ isValid }) => { @@ -60,7 +64,7 @@ export default function Step3Conditions({ onMount={() => setConditionEditorActive(true)} onUnmount={() => setConditionEditorActive(false)} /> -
+ import("components/CodeEditor" /* webpackChunkName: "CodeEditor" */) +); + const additionalVariables = [ { key: "change", @@ -38,14 +43,14 @@ export default function Step4Body({ return ( <> -
+ }> { + value={extensionObject.extensionBody} + minHeight={400} + onChange={(newValue) => { setExtensionObject({ ...extensionObject, - extensionBody: newValue, + extensionBody: newValue || "", }); }} onValidStatusUpdate={({ isValid }) => { @@ -63,7 +68,7 @@ export default function Step4Body({ onMount={() => setBodyEditorActive(true)} onUnmount={() => setBodyEditorActive(false)} /> -
+ - createStyles({ - editorWrapper: { - position: "relative", - minWidth: 400, - minHeight: 100, - height: "calc(100% - 50px)", - border: `1px solid ${theme.palette.divider}`, - borderRadius: theme.shape.borderRadius, - overflow: "hidden", - }, - - saveButton: { - marginTop: theme.spacing(1), - }, - editor: { - // overwrite user-select: none that causes editor not focusable in Safari - userSelect: "auto", - }, - }) -); - -export default function CodeEditor(props: any) { - const { - handleChange, - extraLibs, - height = 400, - script, - onValideStatusUpdate, - diagnosticsOptions, - onUnmount, - onMount, - } = props; - const theme = useTheme(); - const monacoInstance = useMonaco(); - - const [initialEditorValue] = useState(script ?? ""); - const { tableState } = useProjectContext(); - const classes = useStyles(); - - const editorRef = useRef(); - - useEffect(() => { - return () => { - onUnmount?.(); - }; - }, []); - - function handleEditorDidMount(_, editor) { - editorRef.current = editor; - onMount?.(); - } - - const themeTransformer = (theme: string) => { - switch (theme) { - case "dark": - return "vs-dark"; - default: - return theme; - } - }; - - useMemo(async () => { - if (!monacoInstance) { - // useMonaco returns a monaco instance but initialisation is done asynchronously - // dont execute the logic until the instance is initialised - return; - } - - const firestoreDefsFile = await fetch( - `${process.env.PUBLIC_URL}/firestore.d.ts` - ); - const firebaseAuthDefsFile = await fetch( - `${process.env.PUBLIC_URL}/auth.d.ts` - ); - const firebaseStorageDefsFile = await fetch( - `${process.env.PUBLIC_URL}/storage.d.ts` - ); - const firestoreDefs = await firestoreDefsFile.text(); - const firebaseStorageDefs = await firebaseStorageDefsFile.text(); - const firebaseAuthDefs = (await firebaseAuthDefsFile.text()) - ?.replace("export", "declare") - ?.replace("admin.auth", "adminauth"); - - try { - monacoInstance.languages.typescript.javascriptDefaults.addExtraLib( - firestoreDefs - ); - monacoInstance.languages.typescript.javascriptDefaults.addExtraLib( - firebaseAuthDefs - ); - monacoInstance.languages.typescript.javascriptDefaults.addExtraLib( - firebaseStorageDefs - ); - monacoInstance.languages.typescript.javascriptDefaults.setDiagnosticsOptions( - diagnosticsOptions ?? { - noSemanticValidation: true, - noSyntaxValidation: false, - } - ); - // compiler options - monacoInstance.languages.typescript.javascriptDefaults.setCompilerOptions( - { - target: monacoInstance.languages.typescript.ScriptTarget.ES2020, - allowNonTsExtensions: true, - } - ); - if (extraLibs) { - monacoInstance.languages.typescript.javascriptDefaults.addExtraLib( - extraLibs.join("\n"), - "ts:filename/extraLibs.d.ts" - ); - } - monacoInstance.languages.typescript.javascriptDefaults.addExtraLib( - [ - " /**", - " * utility functions", - " */", - ` - declare namespace utilFns { - /** - * Sends out an email through sendGrid - */ - function sendEmail(msg: { - from: string; - templateId: string; - personalizations: { to: string; dynamic_template_data: any }[]; - }): void {} - - /** - * Gets the secret defined in Google Cloud Secret - */ - async function getSecret(name: string, v?: string): any {} - - /** - * Async version of forEach - */ - async function asyncForEach(array: any[], callback: Function): void {} - - /** - * Generate random ID from numbers and English characters including lowercase and uppercase - */ - function generateId(): string {} - - /** - * Add an item to an array field - */ - function arrayUnion(val: string): void {} - - /** - * Remove an item to an array field - */ - function arrayRemove(val: string): void {} - - /** - * Increment a number field - */ - function increment(val: number): void {} - - function hasRequiredFields(requiredFields: string[], data: any): boolean {} - - function hasAnyRole( - authorizedRoles: string[], - context: functions.https.CallableContext - ): boolean {} - } - - `, - ].join("\n"), - "ts:filename/utils.d.ts" - ); - - const rowDefinition = [ - ...Object.keys(tableState?.columns!).map((columnKey: string) => { - const column = tableState?.columns[columnKey]; - switch (column.type) { - case FieldType.shortText: - case FieldType.longText: - case FieldType.email: - case FieldType.phone: - case FieldType.code: - return `${columnKey}:string`; - case FieldType.singleSelect: - const typeString = [ - ...(column.config?.options?.map((opt) => `"${opt}"`) ?? []), - ].join(" | "); - return `${columnKey}:${typeString}`; - case FieldType.multiSelect: - return `${columnKey}:string[]`; - case FieldType.checkbox: - return `${columnKey}:boolean`; - default: - return `${columnKey}:any`; - } - }), - ].join(";\n"); - - const availableFields = Object.keys(tableState?.columns!) - .map((columnKey: string) => `"${columnKey}"`) - .join("|\n"); - - const extensionsDefinition = ` - // basic types that are used in all places - type Row = {${rowDefinition}}; - type Field = ${availableFields} | string | object; - type Fields = Field[]; - type Trigger = "create" | "update" | "delete"; - type Triggers = Trigger[]; - - // function types that defines extension body and should run - type Condition = boolean | ((data: ExtensionContext) => boolean | Promise); - - // the argument that the extension body takes in - type ExtensionContext = { - row: Row; - ref:FirebaseFirestore.DocumentReference; - storage:firebasestorage.Storage; - db:FirebaseFirestore.Firestore; - auth:adminauth.BaseAuth; - change: any; - triggerType: Triggers; - fieldTypes: any; - extensionConfig: { - label: string; - type: string; - triggers: Trigger[]; - conditions: Condition; - requiredFields: string[]; - extensionBody: any; - }; - utilFns: any; - } - - // extension body definition - type slackEmailBody = { - channels?: string[]; - text?: string; - emails: string[]; - blocks?: object[]; - attachments?: any; - } - - type slackChannelBody = { - channels: string[]; - text?: string; - emails?: string[]; - blocks?: object[]; - attachments?: any; - } - - type DocSyncBody = (context: ExtensionContext) => Promise<{ - fieldsToSync: Fields; - row: Row; - targetPath: string; - }> - - type HistorySnapshotBody = (context: ExtensionContext) => Promise<{ - trackedFields: Fields; - }> - - type AlgoliaIndexBody = (context: ExtensionContext) => Promise<{ - fieldsToSync: Fields; - index: string; - row: Row; - objectID: string; - }> - - type MeiliIndexBody = (context: ExtensionContext) => Promise<{ - fieldsToSync: Fields; - index: string; - row: Row; - objectID: string; - }> - - type BigqueryIndexBody = (context: ExtensionContext) => Promise<{ - fieldsToSync: Fields; - index: string; - row: Row; - objectID: string; - }> - - type SlackMessageBody = (context: ExtensionContext) => Promise; - - type SendgridEmailBody = (context: ExtensionContext) => Promise; - - type ApiCallBody = (context: ExtensionContext) => Promise<{ - body: string; - url: string; - method: string; - callback: any; - }> - - type TwilioMessageBody = (context: ExtensionContext) => Promise<{ - body: string; - from: string; - to: string; - }> - - type TaskBody = (context: ExtensionContext) => Promise - `; - - monacoInstance.languages.typescript.javascriptDefaults.addExtraLib( - [ - " /**", - " * extensions type configuration", - " */", - extensionsDefinition, - ].join("\n"), - "ts:filename/extensions.d.ts" - ); - - monacoInstance.languages.typescript.javascriptDefaults.addExtraLib( - [ - " declare var require: any;", - " declare var Buffer: any;", - " const ref:FirebaseFirestore.DocumentReference", - " const storage:firebasestorage.Storage", - " const db:FirebaseFirestore.Firestore;", - " const auth:adminauth.BaseAuth;", - "declare class row {", - " /**", - " * Returns the row fields", - " */", - ...Object.keys(tableState?.columns!).map((columnKey: string) => { - const column = tableState?.columns[columnKey]; - switch (column.type) { - case FieldType.shortText: - case FieldType.longText: - case FieldType.email: - case FieldType.phone: - case FieldType.code: - return `static ${columnKey}:string`; - case FieldType.singleSelect: - const typeString = [ - ...(column.config?.options?.map((opt) => `"${opt}"`) ?? []), - // "string", - ].join(" | "); - return `static ${columnKey}:${typeString}`; - case FieldType.multiSelect: - return `static ${columnKey}:string[]`; - case FieldType.checkbox: - return `static ${columnKey}:boolean`; - default: - return `static ${columnKey}:any`; - } - }), - "}", - ].join("\n"), - "ts:filename/rowFields.d.ts" - ); - } catch (error) { - console.error( - "An error occurred during initialization of Monaco: ", - error - ); - } - }, [tableState?.columns, monacoInstance]); - - function handleEditorValidation(markers) { - if (onValideStatusUpdate) { - onValideStatusUpdate({ - isValid: markers.length <= 0, - }); - } - } - - return ( - <> -
- -
- - ); -} diff --git a/src/components/fields/Action/Settings.tsx b/src/components/fields/Action/Settings.tsx index a7e374fd..35520fa9 100644 --- a/src/components/fields/Action/Settings.tsx +++ b/src/components/fields/Action/Settings.tsx @@ -11,10 +11,7 @@ import FieldSkeleton from "components/SideDrawer/Form/FieldSkeleton"; import { useProjectContext } from "contexts/ProjectContext"; const CodeEditor = lazy( - () => - import( - "components/Table/editors/CodeEditor" /* webpackChunkName: "CodeEditor" */ - ) + () => import("components/CodeEditor" /* webpackChunkName: "CodeEditor" */) ); const Settings = ({ config, handleChange }) => { @@ -93,8 +90,8 @@ const Settings = ({ config, handleChange }) => { action script }> { } else return `static ${param.name}:any`; }), "}", - ], + ].join("\n"), ]} - handleChange={handleChange("script")} + onChange={handleChange("script")} /> { Undo action script }> diff --git a/src/components/fields/Aggregate/Settings.tsx b/src/components/fields/Aggregate/Settings.tsx index 8d8895a8..d1002147 100644 --- a/src/components/fields/Aggregate/Settings.tsx +++ b/src/components/fields/Aggregate/Settings.tsx @@ -6,10 +6,7 @@ import { FieldType } from "constants/fields"; import FieldsDropdown from "components/Table/ColumnMenu/FieldsDropdown"; import { useProjectContext } from "contexts/ProjectContext"; const CodeEditor = lazy( - () => - import( - "components/Table/editors/CodeEditor" /* webpackChunkName: "CodeEditor" */ - ) + () => import("components/CodeEditor" /* webpackChunkName: "CodeEditor" */) ); const Settings = ({ config, handleChange }) => { @@ -29,7 +26,7 @@ const Settings = ({ config, handleChange }) => { Aggergate script }> diff --git a/src/components/fields/Code/SideDrawerField.tsx b/src/components/fields/Code/SideDrawerField.tsx index 5a443701..65696b80 100644 --- a/src/components/fields/Code/SideDrawerField.tsx +++ b/src/components/fields/Code/SideDrawerField.tsx @@ -2,46 +2,18 @@ import { Controller } from "react-hook-form"; import { ISideDrawerFieldProps } from "../types"; import CodeEditor from "components/CodeEditor"; -import { makeStyles, createStyles } from "@mui/styles"; - -const useStyles = makeStyles((theme) => - createStyles({ - wrapper: { - border: "1px solid", - borderColor: - theme.palette.mode === "light" - ? "rgba(0, 0, 0, 0.09)" - : "rgba(255, 255, 255, 0.09)", - - borderRadius: theme.shape.borderRadius, - overflow: "hidden", - }, - }) -); export default function Code({ control, column, disabled, }: ISideDrawerFieldProps) { - const classes = useStyles(); - return ( ( - + )} /> ); diff --git a/src/components/fields/Derivative/Settings.tsx b/src/components/fields/Derivative/Settings.tsx index 807829ab..45e240a3 100644 --- a/src/components/fields/Derivative/Settings.tsx +++ b/src/components/fields/Derivative/Settings.tsx @@ -5,15 +5,12 @@ import FieldSkeleton from "components/SideDrawer/Form/FieldSkeleton"; import { FieldType } from "constants/fields"; import FieldsDropdown from "components/Table/ColumnMenu/FieldsDropdown"; import { useProjectContext } from "contexts/ProjectContext"; -import CodeEditorHelper from "components/CodeEditorHelper"; +import CodeEditorHelper from "@src/components/CodeEditor/CodeEditorHelper"; import { WIKI_LINKS } from "constants/externalLinks"; const CodeEditor = lazy( - () => - import( - "components/Table/editors/CodeEditor" /* webpackChunkName: "CodeEditor" */ - ) + () => import("components/CodeEditor" /* webpackChunkName: "CodeEditor" */) ); const Settings = ({ config, handleChange }) => { @@ -63,10 +60,7 @@ const Settings = ({ config, handleChange }) => { Derivative script }> - + diff --git a/types/custom.ts b/types/custom.ts index ae704ef5..63f9f2d0 100644 --- a/types/custom.ts +++ b/types/custom.ts @@ -6,3 +6,7 @@ declare module "*.mp4" { const content: any; export default content; } +declare module "!!raw-loader!*" { + const content: string; + export default content; +} diff --git a/yarn.lock b/yarn.lock index 5c7be15c..0a60e954 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2374,7 +2374,7 @@ dependencies: state-local "^1.0.6" -"@monaco-editor/react@^4.1.0": +"@monaco-editor/react@^4.3.1": version "4.3.1" resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.3.1.tgz#d65bcbf174c39b6d4e7fec43d0cddda82b70a12a" integrity sha512-f+0BK1PP/W5I50hHHmwf11+Ea92E5H1VZXs+wvKplWUWOfyMa1VVwqkJrXjRvbcqHL+XdIGYWhWNdi4McEvnZg==