consolidate CodeEditor to components/CodeEditor, allow resize, use github themes

This commit is contained in:
Sidney Alcantara
2021-10-20 17:15:49 +11:00
parent fdb1a5178a
commit fe82b159c5
23 changed files with 1526 additions and 630 deletions

View File

@@ -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",

View File

@@ -0,0 +1,10 @@
import SvgIcon, { SvgIconProps } from "@mui/material/SvgIcon";
import { mdiResizeBottomRight } from "@mdi/js";
export default function ResizeBottomRight(props: SvgIconProps) {
return (
<SvgIcon {...props}>
<path d={mdiResizeBottomRight} />
</SvgIcon>
);
}

View File

@@ -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<React.HTMLAttributes<HTMLDivElement>>;
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<any>();
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 (
<div
{...wrapperProps}
className={clsx(classes.editorWrapper, wrapperProps?.className)}
>
<Editor
theme={themeTransformer(theme.palette.mode)}
height={height}
onMount={handleEditorDidMount}
language="javascript"
value={initialEditorValue}
options={{
readOnly: disabled,
fontFamily: theme.typography.fontFamilyMono,
rulers: [80],
minimap: { enabled: false },
...editorOptions,
}}
onChange={onChange as any}
/>
</div>
);
}

View File

@@ -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<boolean>);
// 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<slackEmailBody | slackChannelBody>;
type SendgridEmailBody = (context: ExtensionContext) => Promise<any>;
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<any>;

View File

@@ -844,9 +844,9 @@ declare namespace FirebaseFirestore {
* `exists` property will always be true and `data()` will never return
* 'undefined'.
*/
export class QueryDocumentSnapshot<T = DocumentData> extends DocumentSnapshot<
T
> {
export class QueryDocumentSnapshot<
T = DocumentData
> extends DocumentSnapshot<T> {
private constructor();
/**

View File

@@ -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": []
}

View File

@@ -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": []
}

View File

@@ -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<EditorProps> {
onChange: EditorProps["onChange"];
value: string;
minHeight?: number;
disabled?: boolean;
containerProps?: Partial<BoxProps>;
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 (
<Box
sx={{
minWidth: 400,
minHeight,
height: minHeight,
border: `1px solid ${theme.palette.divider}`,
borderRadius: 1,
resize: "vertical",
overflow: "hidden",
position: "relative",
"& .editor": {
// Overwrite user-select: none that causes editor
// to not be focusable in Safari
userSelect: "auto",
height: "100%",
},
...containerProps?.sx,
}}
>
<Editor
language="javascript"
value={initialEditorValue}
onValidate={onValidate_}
loading={<CircularProgressOptical size={20} sx={{ m: 2 }} />}
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,
}}
/>
<ResizeBottomRightIcon
aria-label="This code editor is resizable"
color="action"
sx={{
position: "absolute",
bottom: 0,
right: 0,
zIndex: 1,
}}
/>
</Box>
);
}

50
src/components/CodeEditor/utils.d.ts vendored Normal file
View File

@@ -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 {}
}

View File

@@ -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" } },
},
}}
>
<MenuItem value="undefined">
<ListItemText
primary="Undefined"
secondary="No default value. The field will not appear in the rows corresponding Firestore document by default."
className={classes.typeSelectItem}
/>
</MenuItem>
<MenuItem value="null">
@@ -78,24 +67,21 @@ export default function DefaultValueInput({
primary="Null"
secondary={
<>
Initialise as <span className={classes.mono}>null</span>.
Initialise as <code>null</code>.
</>
}
className={classes.typeSelectItem}
/>
</MenuItem>
<MenuItem value="static">
<ListItemText
primary="Static"
secondary="Set a specific default value for all cells in this column."
className={classes.typeSelectItem}
/>
</MenuItem>
<MenuItem value="dynamic">
<ListItemText
primary={`Dynamic (Requires ${name} Cloud Functions)`}
secondary={`Write code to set the default value using this tables ${name} Cloud Function. Setup is required.`}
className={classes.typeSelectItem}
/>
</MenuItem>
</TextField>
@@ -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" && (
<>
<CodeEditorHelper docLink={WIKI_LINKS.howToDefaultValues} />
<div className={classes.codeEditorContainer}>
<Suspense fallback={<FieldSkeleton height={100} />}>
<CodeEditor
height={120}
script={config.defaultValue?.script}
handleChange={handleChange("defaultValue.script")}
editorOptions={{
minimap: {
enabled: false,
},
}}
value={config.defaultValue?.script}
onChange={handleChange("defaultValue.script")}
/>
</div>
</Suspense>
</>
)}
</>

View File

@@ -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 }) {
<MenuItem value={WebhookTypes.custom}>Custom</MenuItem>
</Select>
</FormControl>
{state.type === WebhookTypes.custom && (
{/* {state.type === WebhookTypes.custom && (
<CodeEditor
script={state.customParser}
handleChange={handleChange("customParser")}
/>
)}
)} */}
<br />
{state.type === WebhookTypes.typeForm && (
<>

View File

@@ -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 (
<>
<div>
<Suspense fallback={<FieldSkeleton height={200} />}>
<CodeEditor
script={extensionObject.conditions}
height={200}
handleChange={(newValue) => {
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)}
/>
</div>
</Suspense>
<CodeEditorHelper
docLink={WIKI_LINKS.extensions}

View File

@@ -1,12 +1,17 @@
import { lazy, Suspense } from "react";
import { IExtensionModalStepProps } from "./ExtensionModal";
import _upperFirst from "lodash/upperFirst";
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 +43,14 @@ export default function Step4Body({
return (
<>
<div>
<Suspense fallback={<FieldSkeleton height={200} />}>
<CodeEditor
script={extensionObject.extensionBody}
height={400}
handleChange={(newValue) => {
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)}
/>
</div>
</Suspense>
<CodeEditorHelper
docLink={

View File

@@ -1,398 +0,0 @@
import { useRef, useMemo, useState } from "react";
import { makeStyles, createStyles } from "@mui/styles";
import { useTheme } from "@mui/material/styles";
import Editor, { useMonaco } from "@monaco-editor/react";
import { useProjectContext } from "contexts/ProjectContext";
import { FieldType } from "constants/fields";
import { useEffect } from "react";
const useStyles = makeStyles((theme) =>
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<any>();
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<boolean>);
// 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<slackEmailBody | slackChannelBody>;
type SendgridEmailBody = (context: ExtensionContext) => Promise<any>;
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<any>
`;
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 (
<>
<div className={classes.editorWrapper}>
<Editor
theme={themeTransformer(theme.palette.mode)}
onMount={handleEditorDidMount}
language="javascript"
height={height}
value={initialEditorValue}
onChange={handleChange}
onValidate={handleEditorValidation}
options={{
// readOnly: disabled,
fontFamily: theme.typography.fontFamilyMono,
rulers: [80],
minimap: { enabled: false },
// ...editorOptions,
}}
className={classes.editor}
/>
</div>
</>
);
}

View File

@@ -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 }) => {
<Typography variant="overline">action script</Typography>
<Suspense fallback={<FieldSkeleton height={300} />}>
<CodeEditor
height={300}
script={config.script}
minHeight={300}
value={config.script}
extraLibs={[
[
"declare class ref {",
@@ -121,9 +118,9 @@ const Settings = ({ config, handleChange }) => {
} else return `static ${param.name}:any`;
}),
"}",
],
].join("\n"),
]}
handleChange={handleChange("script")}
onChange={handleChange("script")}
/>
</Suspense>
<FormControlLabel
@@ -175,9 +172,9 @@ const Settings = ({ config, handleChange }) => {
<Typography variant="overline">Undo action script</Typography>
<Suspense fallback={<FieldSkeleton height={300} />}>
<CodeEditor
height={300}
script={config["undo.script"]}
handleChange={handleChange("undo.script")}
minHeight={300}
value={config["undo.script"]}
onChange={handleChange("undo.script")}
/>
</Suspense>
</>

View File

@@ -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 }) => {
<Typography variant="overline">Aggergate script</Typography>
<Suspense fallback={<FieldSkeleton height={200} />}>
<CodeEditor
script={
value={
config.script ??
`//triggerType: create | update | delete\n//aggregateState: the subtable accumulator stored in the cell of this column\n//snapshot: the triggered document snapshot of the the subcollection\n//incrementor: short for firebase.firestore.FieldValue.increment(n);\n//This script needs to return the new aggregateState cell value.
switch (triggerType){
@@ -45,13 +42,13 @@ switch (triggerType){
}
extraLibs={[
` /**
* increaments firestore field value
* increments firestore field value
*/",
function incrementor(value:number):number {
}`,
]}
handleChange={handleChange("script")}
onChange={handleChange("script")}
/>
</Suspense>

View File

@@ -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 (
<Controller
control={control}
name={column.key}
render={({ field: { onChange, value } }) => (
<CodeEditor
disabled={disabled}
value={value}
onChange={onChange}
wrapperProps={{ className: classes.wrapper }}
editorOptions={{
minimap: {
enabled: false,
},
}}
/>
<CodeEditor disabled={disabled} value={value} onChange={onChange} />
)}
/>
);

View File

@@ -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 }) => {
<InputLabel>Derivative script</InputLabel>
<CodeEditorHelper docLink={WIKI_LINKS.fieldTypesDerivative} />
<Suspense fallback={<FieldSkeleton height={200} />}>
<CodeEditor
script={config.script}
handleChange={handleChange("script")}
/>
<CodeEditor value={config.script} onChange={handleChange("script")} />
</Suspense>
</div>
</>

View File

@@ -6,3 +6,7 @@ declare module "*.mp4" {
const content: any;
export default content;
}
declare module "!!raw-loader!*" {
const content: string;
export default content;
}

View File

@@ -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==