diff --git a/src/client/App.tsx b/src/client/App.tsx
index d440302..4c1392e 100644
--- a/src/client/App.tsx
+++ b/src/client/App.tsx
@@ -181,7 +181,7 @@ export const App = () => {
Coder
{
{/* EDITOR */}
-
+
diff --git a/src/client/Editor.tsx b/src/client/Editor.tsx
index 87c5fc8..b7068e5 100644
--- a/src/client/Editor.tsx
+++ b/src/client/Editor.tsx
@@ -14,8 +14,7 @@ import {
TooltipTrigger,
} from "@/client/components/Tooltip";
import { useTheme } from "@/client/contexts/theme";
-import { multiSelect, radio, switchInput, textInput } from "@/client/snippets";
-import type { ParameterFormType } from "@/gen/types";
+import { snippets, type SnippetFunc } from "@/client/snippets";
import { cn } from "@/utils/cn";
import { Editor as MonacoEditor } from "@monaco-editor/react";
import {
@@ -23,47 +22,48 @@ import {
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";
+import type { ParameterWithSource } from "@/gen/types";
type EditorProps = {
code: string;
setCode: React.Dispatch>;
+ parameters: ParameterWithSource[];
};
-export const Editor: FC = ({ code, setCode }) => {
+export const Editor: FC = ({ code, setCode, parameters }) => {
const { appliedTheme } = useTheme();
const editorRef = useEditor();
+ const [tab, setTab] = useState(() => "code");
+
const [codeCopied, setCodeCopied] = useState(() => false);
const copyTimeoutId = useRef | undefined>(
undefined,
);
- const [tab, setTab] = useState(() => "code");
-
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 nextInOrder =
+ parameters.reduce(
+ (highestOrder, parameter) =>
+ highestOrder < parameter.order ? parameter.order : highestOrder,
+ 0,
+ ) + 1;
+
+ const nameCount = parameters.filter((p) => p.name.startsWith(name)).length;
+
+ setCode(
+ `${code.trimEnd()}\n\n${snippet(nameCount > 0 ? `${name}-${nameCount}` : name, nextInOrder)}\n`,
+ );
};
useEffect(() => {
@@ -116,23 +116,17 @@ export const Editor: FC = ({ code, setCode }) => {
- onAddSnippet("input")}>
-
- Text input
-
- onAddSnippet("multi-select")}
- >
-
- Multi-select
-
- onAddSnippet("radio")}>
-
- Radio
-
- onAddSnippet("switch")}>
- Switches
-
+ {snippets.map(
+ ({ name, label, icon: Icon, snippet }, index) => (
+ onAddSnippet(name, snippet)}
+ >
+
+ {label}
+
+ ),
+ )}
diff --git a/src/client/Preview.tsx b/src/client/Preview.tsx
index 9ca2d27..3e5e514 100644
--- a/src/client/Preview.tsx
+++ b/src/client/Preview.tsx
@@ -239,7 +239,9 @@ const PreviewEmptyState = () => {
Read the docs
diff --git a/src/client/snippets.ts b/src/client/snippets.ts
index 26b8fef..4c94dbe 100644
--- a/src/client/snippets.ts
+++ b/src/client/snippets.ts
@@ -1,3 +1,14 @@
+import {
+ LetterTextIcon,
+ RadioIcon,
+ Rows3Icon,
+ Settings2Icon,
+ SquareMousePointerIcon,
+ TagIcon,
+ TextCursorInputIcon,
+ ToggleLeftIcon,
+} from "lucide-react";
+
export const defaultCode = `terraform {
required_providers {
coder = {
@@ -7,25 +18,104 @@ export const defaultCode = `terraform {
}
}`;
-export const textInput = `data "coder_parameter" "project-name" {
- display_name = "An input"
- name = "an-input"
- description = "What is the name of your project?"
- order = 4
+export type SnippetFunc = (name?: string, order?: number) => string;
+type Snippet = {
+ name: string;
+ label: string;
+ icon: typeof RadioIcon;
+ snippet: SnippetFunc;
+};
+
+export const input: SnippetFunc = (
+ name = "input",
+ order = 1,
+) => `data "coder_parameter" "text-input" {
+ name = "${name}"
+ display_name = "A text input"
+ description = "This parameter can be used to input text."
+ order = ${order}
+
+ styling = jsonencode({
+ placeholder = "A placeholder that will appear if the input value is empty"
+ })
form_type = "input"
type = "string"
default = "An input value"
}`;
-export const radio = `data "coder_parameter" "radio" {
- name = "radio"
- display_name = "An example of a radio input"
- description = "The next parameter supports a single value."
- type = "string"
- form_type = "radio"
- order = 1
- default = "option-1"
+export const textarea: SnippetFunc = (
+ name = "textarea",
+ order = 1,
+) => `data "coder_parameter" "textarea" {
+ name = "${name}"
+ display_name = "A textarea input"
+ description = "This parameter can be used to input multiple lines of text"
+ order = ${order}
+
+ styling = jsonencode({
+ placeholder = "A placeholder that will appear if the input value is empty"
+ })
+
+ form_type = "textarea"
+ type = "string"
+ default = "An input value"
+}`;
+
+export const radio: SnippetFunc = (
+ name = "radio",
+ order = 1,
+) => `data "coder_parameter" "radio" {
+ name = "${name}"
+ display_name = "A radio input"
+ description = "This parameter supports selecting a single value out of a list of options"
+ order = ${order}
+
+ type = "string"
+ form_type = "radio"
+ default = "option-1"
+
+ option {
+ name = "Option 1"
+ value = "option-1"
+ description = "A description for Option 1"
+ }
+
+ option {
+ name = "Option 2"
+ value = "option-2"
+ description = "A description for Option 2"
+ }
+
+ option {
+ name = "Option 3"
+ value = "option-3"
+ description = "A description for Option 3"
+ }
+
+ option {
+ name = "Option 4"
+ value = "option-4"
+ description = "A description for Option 4"
+ }
+}`;
+
+export const dropdown: SnippetFunc = (
+ name = "dropdown",
+ order = 1,
+) => `data "coder_parameter" "dropdown" {
+ name = "${name}"
+ display_name = "A dropdown input"
+ description = "This parameter supports selecting a single value out of a list of options. Especially useful when you have a lot of options."
+ order = ${order}
+
+ styling = jsonencode({
+ placeholder = "A placeholder that will appear if the input value is empty"
+ })
+
+ type = "string"
+ form_type = "dropdown"
+ default = "option-1"
option {
name = "Option 1"
@@ -52,13 +142,17 @@ export const radio = `data "coder_parameter" "radio" {
}
}`;
-export const multiSelect = `data "coder_parameter" "multi-select" {
- name = "multi-select"
- display_name = "An example of a multi-select"
- description = "The next parameter supports multiple values."
+export const multiSelect: SnippetFunc = (
+ name = "multi-select",
+ order = 1,
+) => `data "coder_parameter" "multi-select" {
+ name = "${name}"
+ display_name = "A multi-select input"
+ description = "This parameter supports selecting multiple values from a list of options"
+ order = ${order}
+
type = "list(string)"
form_type = "multi-select"
- order = 1
option {
name = "Option 1"
@@ -85,15 +179,101 @@ export const multiSelect = `data "coder_parameter" "multi-select" {
}
}`;
-export const switchInput = `data "coder_parameter" "switch" {
- name = "switch"
- display_name = "An example of a switch"
- description = "The next parameter can be on or off"
+export const tagSelect: SnippetFunc = (
+ name = "tag-select",
+ order = 1,
+) => `data "coder_parameter" "tag-select" {
+ name = "${name}"
+ display_name = "A tag-select input"
+ description = "This parameter supports selecting multiple user inputed values at once"
+ order = ${order}
+
+ type = "list(string)"
+ form_type = "tag-select"
+}`;
+
+export const switchInput: SnippetFunc = (
+ name = "switch",
+ order = 1,
+) => `data "coder_parameter" "switch" {
+ name = "${name}"
+ display_name = "A switch input"
+ description = "This parameter can be toggled between true and false"
+ order = ${order}
+
type = "bool"
form_type = "switch"
default = true
- order = 1
-}`
+}`;
+
+export const slider: SnippetFunc = (
+ name = "slider",
+ order = 1,
+) => `data "coder_parameter" "slider" {
+ name = "${name}"
+ display_name = "A slider input"
+ description = "This parameter supports selecting a number within a given range"
+ type = "number"
+ form_type = "slider"
+ default = 6
+ order = ${order}
+
+ validation {
+ min = 1
+ max = 10
+ }
+}`;
+
+export const snippets: Snippet[] = [
+ {
+ name: "text-input",
+ label: "Text Input",
+ icon: TextCursorInputIcon,
+ snippet: input,
+ },
+ {
+ name: "textarea",
+ label: "Textarea",
+ icon: LetterTextIcon,
+ snippet: textarea,
+ },
+ {
+ name: "radio",
+ label: "Radio",
+ icon: RadioIcon,
+ snippet: radio,
+ },
+ {
+ name: "switch",
+ label: "Multi-select",
+ icon: SquareMousePointerIcon,
+ snippet: multiSelect,
+ },
+ {
+ name: "tag-select",
+ label: "Tag-select",
+ icon: TagIcon,
+ snippet: tagSelect,
+ },
+ {
+ name: "switch",
+ label: "Switch",
+ icon: ToggleLeftIcon,
+ snippet: switchInput,
+ },
+ {
+ name: "dropdown",
+ label: "Dropdown",
+ icon: Rows3Icon,
+ snippet: dropdown,
+ },
+ {
+ name: "slider",
+ label: "Slider",
+ icon: Settings2Icon,
+ snippet: slider,
+ },
+];
export const checkerModule = `
variable "solutions" {