-
Notifications
You must be signed in to change notification settings - Fork 2
feat: add snippets for all form types and add docs links #34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
3cdaf7b
a5145a0
3d01957
285fd2a
aca5049
684d476
bcdb981
0159d41
ba502b3
15485d7
fac0ff0
c52f435
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,13 @@ | ||
import { Editor as MonacoEditor } from "@monaco-editor/react"; | ||
import { | ||
CheckIcon, | ||
ChevronDownIcon, | ||
CopyIcon, | ||
FileJsonIcon, | ||
SettingsIcon, | ||
ZapIcon, | ||
} from "lucide-react"; | ||
import { type FC, useEffect, useState } from "react"; | ||
import { Button } from "@/client/components/Button"; | ||
import { | ||
DropdownMenu, | ||
|
@@ -13,71 +23,50 @@ import { | |
TooltipContent, | ||
TooltipTrigger, | ||
} from "@/client/components/Tooltip"; | ||
import { useEditor } from "@/client/contexts/editor"; | ||
import { useTheme } from "@/client/contexts/theme"; | ||
import { multiSelect, radio, switchInput, textInput } from "@/client/snippets"; | ||
import type { ParameterFormType } from "@/gen/types"; | ||
import { type SnippetFunc, snippets } from "@/client/snippets"; | ||
import type { ParameterWithSource } from "@/gen/types"; | ||
import { cn } from "@/utils/cn"; | ||
import { Editor as MonacoEditor } from "@monaco-editor/react"; | ||
import { | ||
CheckIcon, | ||
ChevronDownIcon, | ||
CopyIcon, | ||
FileJsonIcon, | ||
RadioIcon, | ||
SettingsIcon, | ||
SquareMousePointerIcon, | ||
TextCursorInputIcon, | ||
ToggleLeftIcon, | ||
ZapIcon, | ||
} from "lucide-react"; | ||
import { type FC, useEffect, useRef, useState } from "react"; | ||
import { useEditor } from "@/client/contexts/editor"; | ||
|
||
type EditorProps = { | ||
code: string; | ||
setCode: React.Dispatch<React.SetStateAction<string>>; | ||
parameters: ParameterWithSource[]; | ||
}; | ||
|
||
export const Editor: FC<EditorProps> = ({ code, setCode }) => { | ||
export const Editor: FC<EditorProps> = ({ code, setCode, parameters }) => { | ||
const { appliedTheme } = useTheme(); | ||
const editorRef = useEditor(); | ||
|
||
const [codeCopied, setCodeCopied] = useState(() => false); | ||
const copyTimeoutId = useRef<ReturnType<typeof setTimeout> | undefined>( | ||
undefined, | ||
); | ||
|
||
const [tab, setTab] = useState(() => "code"); | ||
|
||
const [codeCopied, setCodeCopied] = useState(() => false); | ||
|
||
const onCopy = () => { | ||
navigator.clipboard.writeText(code); | ||
setCodeCopied(() => true); | ||
}; | ||
|
||
const onAddSnippet = (formType: ParameterFormType) => { | ||
if (formType === "input") { | ||
setCode(`${code.trimEnd()}\n\n${textInput}\n`); | ||
} else if (formType === "radio") { | ||
setCode(`${code.trimEnd()}\n\n${radio}\n`); | ||
} else if (formType === "multi-select") { | ||
setCode(`${code.trimEnd()}\n\n${multiSelect}\n`); | ||
} else if (formType === "switch") { | ||
setCode(`${code.trimEnd()}\n\n${switchInput}\n`); | ||
} | ||
const onAddSnippet = (name: string, snippet: SnippetFunc) => { | ||
const nameCount = parameters.filter((p) => p.name.startsWith(name)).length; | ||
|
||
const nextInOrder = 1 + Math.max(0, ...parameters.map((p) => p.order)); | ||
const newName = nameCount > 0 ? `${name}-${nameCount}` : name; | ||
const newSnippet = snippet(newName, nextInOrder); | ||
setCode(`${code.trimEnd()}\n\n${newSnippet}\n`); | ||
}; | ||
|
||
useEffect(() => { | ||
if (!codeCopied) { | ||
return; | ||
} | ||
|
||
clearTimeout(copyTimeoutId.current); | ||
|
||
copyTimeoutId.current = setTimeout(() => { | ||
const copyTimeoutId = setTimeout(() => { | ||
setCodeCopied(() => false); | ||
}, 1000); | ||
|
||
return () => clearTimeout(copyTimeoutId.current); | ||
return () => clearTimeout(copyTimeoutId); | ||
}, [codeCopied]); | ||
|
||
return ( | ||
|
@@ -116,23 +105,17 @@ export const Editor: FC<EditorProps> = ({ code, setCode }) => { | |
|
||
<DropdownMenuPortal> | ||
<DropdownMenuContent align="start"> | ||
<DropdownMenuItem onClick={() => onAddSnippet("input")}> | ||
<TextCursorInputIcon width={24} height={24} /> | ||
Text input | ||
</DropdownMenuItem> | ||
<DropdownMenuItem | ||
onClick={() => onAddSnippet("multi-select")} | ||
> | ||
<SquareMousePointerIcon width={24} height={24} /> | ||
Multi-select | ||
</DropdownMenuItem> | ||
<DropdownMenuItem onClick={() => onAddSnippet("radio")}> | ||
<RadioIcon width={24} height={24} /> | ||
Radio | ||
</DropdownMenuItem> | ||
<DropdownMenuItem onClick={() => onAddSnippet("switch")}> | ||
<ToggleLeftIcon width={24} height={24} /> Switches | ||
</DropdownMenuItem> | ||
{snippets.map( | ||
({ name, label, icon: Icon, snippet }, index) => ( | ||
<DropdownMenuItem | ||
key={index} | ||
Comment on lines
+109
to
+111
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If names aren't unique, I feel like it'd be better to find a way to generate a unique ID as a snippet is getting added There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The snippets array doesn't change, it's a set list the user can pick from. Unless you're talking about |
||
onClick={() => onAddSnippet(name, snippet)} | ||
> | ||
<Icon size={24} /> | ||
{label} | ||
</DropdownMenuItem> | ||
), | ||
)} | ||
</DropdownMenuContent> | ||
</DropdownMenuPortal> | ||
</DropdownMenu> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -239,9 +239,12 @@ const PreviewEmptyState = () => { | |
</p> | ||
</div> | ||
<a | ||
href="#todo" | ||
href="https://coder.com/docs/admin/templates/extending-templates/parameters" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same note here about links that open in new tabs |
||
target="_blank" | ||
rel="noreferrer" | ||
className="flex items-center gap-0.5 text-content-link text-sm" | ||
> | ||
<span className="sr-only"> (link opens in new tab)</span> | ||
Read the docs | ||
<span className="inline"> | ||
<ExternalLinkIcon width={16} /> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: for accessibility, whenever you have an external link, you want to add some screen reader text to make sure saying that the link opens in a new tab
It doesn't have to be much. It can be something like: