diff --git a/.changeset/fresh-rings-occur.md b/.changeset/fresh-rings-occur.md new file mode 100644 index 0000000000..9e466e66ad --- /dev/null +++ b/.changeset/fresh-rings-occur.md @@ -0,0 +1,7 @@ +--- +"@quri/versioned-squiggle-components": patch +"@quri/squiggle-components": patch +"@quri/ui": patch +--- + +Updated playground styles to look more professional, added StyledToggle diff --git a/packages/components/src/components/CodeEditor/focusGutterExtension.tsx b/packages/components/src/components/CodeEditor/focusGutterExtension.tsx index b2bb5d2b7a..8242acaf50 100644 --- a/packages/components/src/components/CodeEditor/focusGutterExtension.tsx +++ b/packages/components/src/components/CodeEditor/focusGutterExtension.tsx @@ -125,10 +125,10 @@ class FocusableMarker extends GutterMarker {
diff --git a/packages/components/src/components/SquiggleEditor.tsx b/packages/components/src/components/SquiggleEditor.tsx index 797d9aa1c0..c06e36789c 100644 --- a/packages/components/src/components/SquiggleEditor.tsx +++ b/packages/components/src/components/SquiggleEditor.tsx @@ -56,7 +56,7 @@ export const SquiggleEditor: FC = ({ return (
( /> + > + Format + ( @@ -118,9 +119,7 @@ export const LeftPlaygroundPanel = forwardRef( )} > - - Menu - + Menu
{props.renderExtraControls?.({ openModal })} diff --git a/packages/components/src/components/SquigglePlayground/ResizableTwoPanelLayout.tsx b/packages/components/src/components/SquigglePlayground/ResizableTwoPanelLayout.tsx index 7f0f266d92..c51219debc 100644 --- a/packages/components/src/components/SquigglePlayground/ResizableTwoPanelLayout.tsx +++ b/packages/components/src/components/SquigglePlayground/ResizableTwoPanelLayout.tsx @@ -91,7 +91,7 @@ export const ResizableTwoPanelLayout: FC = ({ >
diff --git a/packages/components/src/components/SquigglePlayground/index.tsx b/packages/components/src/components/SquigglePlayground/index.tsx index a08dac667e..bf163e0290 100644 --- a/packages/components/src/components/SquigglePlayground/index.tsx +++ b/packages/components/src/components/SquigglePlayground/index.tsx @@ -195,7 +195,7 @@ export const SquigglePlayground: React.FC = ( } else { return (
- +
); } diff --git a/packages/components/src/components/SquiggleViewer/ValueViewer/Title.tsx b/packages/components/src/components/SquiggleViewer/ValueViewer/Title.tsx index 9015b2cce5..e61da606ae 100644 --- a/packages/components/src/components/SquiggleViewer/ValueViewer/Title.tsx +++ b/packages/components/src/components/SquiggleViewer/ValueViewer/Title.tsx @@ -29,9 +29,8 @@ type StandardProps = { title?: string; color?: string; textSize?: string; - font?: string; icon?: JSX.Element; - showColon?: boolean; + hasEdges?: boolean; isFocusEnabled?: boolean; }; @@ -51,18 +50,18 @@ const getStandardProps = (props: TitleProps): StandardProps => { viewerType === "tooltip" && { isFocusEnabled: false }, valuePath.edges.length > 1 && { color: "text-teal-700", - showColon: true, + hasEdges: true, }, headerVisibility === "large" && { color: "text-stone-700", textSize: "text-md font-bold", - showColon: false, + hasEdges: false, isFocusEnabled: false, }, isRoot && { color: "text-stone-500", textSize: "text-sm font-semibold", - showColon: false, + hasEdges: false, isFocusEnabled: false, }, parentValue?.tag === "Array" && @@ -71,13 +70,12 @@ const getStandardProps = (props: TitleProps): StandardProps => { }, taggedName && { title: taggedName, - font: "font-sans", }, isRootImport && { title: exportData?.sourceId || undefined, - color: "text-violet-900", - icon: , - showColon: false, + color: "text-cyan-600", + icon: , + hasEdges: false, }, ]); @@ -91,17 +89,16 @@ export const Title: FC = (props) => { title = pathToShortName(valuePath), color = "text-orange-900", icon = undefined, - showColon = false, + hasEdges = false, isFocusEnabled = true, textSize = "text-sm", - font = "font-mono", } = standards; return (
{icon} @@ -109,14 +106,13 @@ export const Title: FC = (props) => { className={clsx( color, textSize, - font, + "font-mono", isFocusEnabled && "cursor-pointer hover:underline" )} onClick={() => isFocusEnabled && zoomIn(valuePath)} > {title}
- {showColon &&
:
}
); }; diff --git a/packages/components/src/components/SquiggleViewer/ValueViewer/WithContext.tsx b/packages/components/src/components/SquiggleViewer/ValueViewer/WithContext.tsx index 83443eb342..6129270c4e 100644 --- a/packages/components/src/components/SquiggleViewer/ValueViewer/WithContext.tsx +++ b/packages/components/src/components/SquiggleViewer/ValueViewer/WithContext.tsx @@ -137,12 +137,12 @@ export const ValueWithContextViewer: FC = ({ return (
- +
); } else { @@ -158,7 +158,7 @@ export const ValueWithContextViewer: FC = ({ className="group flex w-4 shrink-0 cursor-pointer justify-center" onClick={handle.toggleCollapsed} > -
+
); } else { @@ -178,10 +178,8 @@ export const ValueWithContextViewer: FC = ({ }} tabIndex={viewerType === "tooltip" ? undefined : 0} className={clsx( - "group flex justify-between rounded-sm pr-0.5 hover:bg-stone-100 focus-visible:outline-none", - isZoomedIn - ? "mb-2 px-0.5 py-1 focus:bg-indigo-50" - : "focus:bg-indigo-100" + "group flex justify-between rounded-sm pr-0.5 hover:bg-gray-100 focus:bg-slate-100 focus-visible:outline-none", + isZoomedIn && "mb-2 px-0.5 py-1" )} onFocus={(_) => { scrollEditorToPath(); @@ -239,7 +237,7 @@ export const ValueWithContextViewer: FC = ({ className={clsx( "cursor-pointer transition", isRootImport - ? "text-violet-400 hover:!text-violet-900 group-hover:text-violet-500 group-focus:text-violet-600" + ? "text-cyan-200 hover:!text-cyan-900 group-hover:text-cyan-700 group-focus:text-cyan-700" : "text-slate-200 hover:!text-slate-900 group-hover:text-slate-400 group-focus:text-slate-400" )} /> diff --git a/packages/components/src/components/SquiggleViewer/ZoomedInNavigation.tsx b/packages/components/src/components/SquiggleViewer/ZoomedInNavigation.tsx index c7de1d4384..ad2f2c839b 100644 --- a/packages/components/src/components/SquiggleViewer/ZoomedInNavigation.tsx +++ b/packages/components/src/components/SquiggleViewer/ZoomedInNavigation.tsx @@ -16,7 +16,9 @@ export const ZoomedInNavigationItem: FC<{ > {text} - + + +
); @@ -32,7 +34,7 @@ export const ZoomedInNavigation: FC<{ : zoomedInPath.allPrefixPaths(); return ( -
+
{/* We remove the last element, because it's the one being shown */} diff --git a/packages/components/src/components/ViewerWithMenuBar/Layout.tsx b/packages/components/src/components/ViewerWithMenuBar/Layout.tsx index 286b90ed21..064e5bd85f 100644 --- a/packages/components/src/components/ViewerWithMenuBar/Layout.tsx +++ b/packages/components/src/components/ViewerWithMenuBar/Layout.tsx @@ -13,16 +13,17 @@ export const Layout: FC<{
- {menu} +
{menu}
{indicator} {changeSeedAndRunButton}
+
= ({ isSimulating, }) => { const props = { - size: 16, + size: 14, className: clsx( isSimulating - ? "animate-spin text-violet-400 group-hover:text-violet-900" - : "text-violet-200 group-hover:text-violet-500" + ? "animate-spin text-gray-500 group-hover:text-gray-600" + : "text-gray-300 group-hover:text-gray-600" ), }; switch (side) { diff --git a/packages/components/src/components/ViewerWithMenuBar/SimulatingIndicator.tsx b/packages/components/src/components/ViewerWithMenuBar/SimulatingIndicator.tsx index d97deaa5b7..a68008121e 100644 --- a/packages/components/src/components/ViewerWithMenuBar/SimulatingIndicator.tsx +++ b/packages/components/src/components/ViewerWithMenuBar/SimulatingIndicator.tsx @@ -16,11 +16,11 @@ export const SimulatingIndicator: FC<{ return (
- {`simulation #${simulation.executionId} in ${showTime( + {`Simulation #${simulation.executionId} in ${showTime( simulation.executionTime )}`}
diff --git a/packages/components/src/components/ViewerWithMenuBar/ViewerMenu.tsx b/packages/components/src/components/ViewerWithMenuBar/ViewerMenu.tsx index fb63bc0d51..d7962b4491 100644 --- a/packages/components/src/components/ViewerWithMenuBar/ViewerMenu.tsx +++ b/packages/components/src/components/ViewerWithMenuBar/ViewerMenu.tsx @@ -2,17 +2,15 @@ import clsx from "clsx"; import { FC } from "react"; import { - Button, - CodeBracketIcon, Dropdown, DropdownMenu, DropdownMenuActionItem, DropdownMenuHeader, - TriangleIcon, } from "@quri/ui"; import { SqOutputResult } from "../../../../squiggle-lang/src/public/types.js"; import { SelectableViewerTab, ViewerTab } from "../../lib/utility.js"; +import { ToolbarItem } from "../ui/PanelWithToolbar/ToolbarItem.js"; const MenuItemTitle: FC<{ title: string; type: string | null }> = ({ title, @@ -21,11 +19,11 @@ const MenuItemTitle: FC<{ title: string; type: string | null }> = ({ const isEmpty = type === null; return (
- {title} + {title} {isEmpty ? ( - Empty + Empty ) : ( - {type} + {type} )}
); @@ -78,12 +76,27 @@ export const ViewerMenu: FC = ({ return null; } }; + const viewerName = () => { + switch (tab) { + case "Imports": + return "Imports"; + case "Variables": + return "Local Variables"; + case "Exports": + return "Exported Variables"; + case "Result": + return "Final Result"; + default: + return ""; + } + }; return ( tab !== "AST" && ( } + title={ + + } onClick={() => { setViewerTab(tab); close(); @@ -94,7 +107,6 @@ export const ViewerMenu: FC = ({ })} Debugging } onClick={() => { setViewerTab("AST"); @@ -104,12 +116,7 @@ export const ViewerMenu: FC = ({ )} > - + {viewerTabTitle(viewerTab)} ); }; diff --git a/packages/components/src/components/ui/Alert.tsx b/packages/components/src/components/ui/Alert.tsx index 10fc68e4f3..56c0aba6b5 100644 --- a/packages/components/src/components/ui/Alert.tsx +++ b/packages/components/src/components/ui/Alert.tsx @@ -80,10 +80,10 @@ export const SuccessAlert: React.FC<{ }> = (props) => ( ); diff --git a/packages/components/src/components/ui/FormSection.tsx b/packages/components/src/components/ui/FormSection.tsx index aa4f0d8707..0c7ced8a83 100644 --- a/packages/components/src/components/ui/FormSection.tsx +++ b/packages/components/src/components/ui/FormSection.tsx @@ -5,7 +5,7 @@ export const FormSection: React.FC<{ children: React.ReactNode; }> = ({ title, children }) => (
-
+
{title}
{children}
diff --git a/packages/components/src/components/ui/PanelWithToolbar/ToolbarItem.tsx b/packages/components/src/components/ui/PanelWithToolbar/ToolbarItem.tsx index e2b35535a9..066eacb1a0 100644 --- a/packages/components/src/components/ui/PanelWithToolbar/ToolbarItem.tsx +++ b/packages/components/src/components/ui/PanelWithToolbar/ToolbarItem.tsx @@ -1,7 +1,7 @@ import { clsx } from "clsx"; import { ComponentType, FC, ReactNode } from "react"; -import { TextTooltip } from "@quri/ui"; +import { ChevronRightIcon, TextTooltip } from "@quri/ui"; type Props = { icon?: ComponentType<{ className?: string }>; @@ -11,6 +11,7 @@ type Props = { onClick?: () => void; tooltipText?: string; className?: string; + showDropdownArrow?: boolean; }; export const ToolbarItem: FC = ({ @@ -20,6 +21,7 @@ export const ToolbarItem: FC = ({ className, onClick, tooltipText, + showDropdownArrow = false, children, }) => { const withTooltip = (jsx: JSX.Element) => ( @@ -31,7 +33,7 @@ export const ToolbarItem: FC = ({ const main = (
= ({ )} {children &&
{children}
} + {showDropdownArrow && ( + + )}
); diff --git a/packages/components/src/components/ui/PanelWithToolbar/index.tsx b/packages/components/src/components/ui/PanelWithToolbar/index.tsx index b26128b057..55a2ee78e6 100644 --- a/packages/components/src/components/ui/PanelWithToolbar/index.tsx +++ b/packages/components/src/components/ui/PanelWithToolbar/index.tsx @@ -43,7 +43,7 @@ export function PanelWithToolbar({ > ← Back -
+
{modal.title}
@@ -52,7 +52,7 @@ export function PanelWithToolbar({ return (
-
+
{modal ? ( modalHeader ) : ( diff --git a/packages/components/src/styles/common.css b/packages/components/src/styles/common.css index 8bf7a040e4..bf52394743 100644 --- a/packages/components/src/styles/common.css +++ b/packages/components/src/styles/common.css @@ -40,11 +40,11 @@ } .cm-gutterElement.cm-activeLineGutter { - background-color: #abd3e857; + background-color: #f1f5f9; } .ͼ2 .cm-activeLine { - background-color: #abd3e857; + background-color: #f1f5f9; } .cm-gutter.cm-lineNumbers { diff --git a/packages/components/src/widgets/SpecificationWidget.tsx b/packages/components/src/widgets/SpecificationWidget.tsx index 3c04bee456..9647752e34 100644 --- a/packages/components/src/widgets/SpecificationWidget.tsx +++ b/packages/components/src/widgets/SpecificationWidget.tsx @@ -51,7 +51,7 @@ const SpecificationDropdownContent: FC<{ "inline-flex items-center space-x-2 rounded-sm px-2 py-0.5 text-sm font-medium", hasError ? "bg-red-100 text-red-900" - : "bg-green-100 text-green-900" + : "bg-emerald-100 text-emerald-900" )} > {hasError ? : } @@ -103,11 +103,11 @@ const SpecificationView: FC<{ specification: SqSpecification }> = ({ }) => { return (
-
+
Specification
-
+
{specification.name}
{specification.documentation && ( @@ -129,27 +129,27 @@ const SpecificationStatusPreview: FC<{ "flex cursor-pointer flex-row items-center space-x-1.5 rounded-sm px-0.5 transition", hasError ? "bg-red-100 hover:bg-red-300" - : "bg-green-100 hover:bg-green-300" + : "bg-emerald-100 hover:bg-emerald-300" )} > {hasError ? ( ) : ( - + )}
); }; widgetRegistry.register("Specification", { - Preview: () => , + Preview: () => , Chart: (value) => (
diff --git a/packages/hub/src/app/benchmarks/page.tsx b/packages/hub/src/app/benchmarks/page.tsx index c86e35069d..72f1b0be4b 100644 --- a/packages/hub/src/app/benchmarks/page.tsx +++ b/packages/hub/src/app/benchmarks/page.tsx @@ -118,7 +118,7 @@ const Benchmark: FC<{ version: SquiggleVersion; code: string }> = ({ ) : outcome ? (
{outcome.ok ? ( - + ) : ( )} diff --git a/packages/hub/src/app/models/[owner]/[slug]/EditSquiggleSnippetModel.tsx b/packages/hub/src/app/models/[owner]/[slug]/EditSquiggleSnippetModel.tsx index cab2fbbdaf..cb7d306377 100644 --- a/packages/hub/src/app/models/[owner]/[slug]/EditSquiggleSnippetModel.tsx +++ b/packages/hub/src/app/models/[owner]/[slug]/EditSquiggleSnippetModel.tsx @@ -4,6 +4,7 @@ import { BaseSyntheticEvent, FC, use, useMemo, useState } from "react"; import { FormProvider, useFieldArray, useForm } from "react-hook-form"; import { graphql, useFragment } from "react-relay"; +import { PlaygroundToolbarItem } from "@quri/squiggle-components"; import { ButtonWithDropdown, CommentIcon, @@ -17,9 +18,9 @@ import { } from "@quri/ui"; import { checkSquiggleVersion, - SquigglePlaygroundVersionPicker, + SquigglePlaygroundVersionPickerDropdown, type SquiggleVersion, - SquiggleVersionShower, + uncheckedVersionTitle, useAdjustSquiggleVersion, versionedSquigglePackages, versionSupportsDropdownMenu, @@ -270,6 +271,8 @@ export const EditSquiggleSnippetModel: FC = ({ } }; + const codeHasChanged = form.watch("code") !== content.code; + // We don't want to control SquigglePlayground, it's uncontrolled by design. // Instead, we reset the `defaultCode` that we pass to it when version is changed or draft is restored. const [defaultCode, setDefaultCode] = useState(content.code); @@ -326,12 +329,14 @@ export const EditSquiggleSnippetModel: FC = ({ renderExtraControls: () => (
{model.isEditable || forceVersionPicker ? ( - + > + + {uncheckedVersionTitle(version)} + + ) : ( = ({ offset={5} > {/* div wrapper is required because TextTooltip clones its children and SquiggleVersionShower doesn't forwardRef */} -
- +
+ + {uncheckedVersionTitle(version)}{" "} +
)} {model.isEditable && ( - + )}
), diff --git a/packages/hub/src/app/models/[owner]/[slug]/revisions/ModelRevisionsList.tsx b/packages/hub/src/app/models/[owner]/[slug]/revisions/ModelRevisionsList.tsx index b82393d5fb..55032b646c 100644 --- a/packages/hub/src/app/models/[owner]/[slug]/revisions/ModelRevisionsList.tsx +++ b/packages/hub/src/app/models/[owner]/[slug]/revisions/ModelRevisionsList.tsx @@ -85,7 +85,7 @@ const ModelRevisionItem: FC<{
{`Build Time: ${revision.lastBuild.runSeconds.toFixed(2)}s`}
)} {revision.variableRevisions.length > 0 ? ( -
+
{`${revision.variableRevisions.length} variables`}
) : null} diff --git a/packages/hub/src/components/layout/RootLayout/PageMenuLink.tsx b/packages/hub/src/components/layout/RootLayout/PageMenuLink.tsx index ce9ff17a78..38bab96c58 100644 --- a/packages/hub/src/components/layout/RootLayout/PageMenuLink.tsx +++ b/packages/hub/src/components/layout/RootLayout/PageMenuLink.tsx @@ -34,7 +34,7 @@ export const PageMenuLink: FC = ({ const Icon = icon; return mode === "desktop" ? ( diff --git a/packages/ui/package.json b/packages/ui/package.json index a193bd8d98..79e2b95893 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -108,6 +108,16 @@ "no-console": "error", "react/react-in-jsx-scope": "off", "react/prop-types": "off" - } + }, + "overrides": [ + { + "files": [ + "src/stories/**/*.tsx" + ], + "rules": { + "no-console": "off" + } + } + ] } } diff --git a/packages/ui/src/components/Button.tsx b/packages/ui/src/components/Button.tsx index f697505a3d..7f1aa66f3b 100644 --- a/packages/ui/src/components/Button.tsx +++ b/packages/ui/src/components/Button.tsx @@ -37,15 +37,15 @@ export const Button: FC = ({ className={clsx( "border text-sm font-medium", theme === "primary" - ? "border-green-900 bg-green-700 text-white" - : "border-slate-300 bg-slate-100 text-gray-600", + ? "border-emerald-600 bg-emerald-500 text-white" + : "border-gray-300 bg-gray-50 text-gray-500", disabled ? "opacity-60" : theme === "primary" - ? "hover:border-green-800 hover:bg-green-800 hover:text-white" - : "hover:bg-slate-200 hover:text-gray-900", + ? "hover:bg-emerald-600" + : "hover:bg-gray-200 hover:text-gray-900", wide && "w-full", - size === "medium" && "h-8 rounded-md", + size === "medium" && "h-8 rounded-sm", size === "small" && "h-6 rounded-sm", // This could probably be simplified, but I'm not sure how. // Tailwind group-* styles don't allow styling based on parent, only on parent state. diff --git a/packages/ui/src/components/Dropdown/DropdownMenuHeader.tsx b/packages/ui/src/components/Dropdown/DropdownMenuHeader.tsx index 6093316aed..8ba52c669f 100644 --- a/packages/ui/src/components/Dropdown/DropdownMenuHeader.tsx +++ b/packages/ui/src/components/Dropdown/DropdownMenuHeader.tsx @@ -2,8 +2,6 @@ import { FC, PropsWithChildren } from "react"; export const DropdownMenuHeader: FC = ({ children }) => { return ( -
- {children} -
+
{children}
); }; diff --git a/packages/ui/src/components/Dropdown/DropdownMenuItemLayout.tsx b/packages/ui/src/components/Dropdown/DropdownMenuItemLayout.tsx index e98fe36cd8..9a8127708e 100644 --- a/packages/ui/src/components/Dropdown/DropdownMenuItemLayout.tsx +++ b/packages/ui/src/components/Dropdown/DropdownMenuItemLayout.tsx @@ -17,7 +17,7 @@ const iconDisplay = (icon?: FC, acting?: boolean) => { @@ -30,9 +30,9 @@ export const DropdownMenuItemLayout: FC = ({ acting, }) => { return ( -
+
{iconDisplay(icon, acting)} -
+
{title}
diff --git a/packages/ui/src/components/Dropdown/DropdownMenuSeparator.tsx b/packages/ui/src/components/Dropdown/DropdownMenuSeparator.tsx index 8bb2e89881..9a408af82a 100644 --- a/packages/ui/src/components/Dropdown/DropdownMenuSeparator.tsx +++ b/packages/ui/src/components/Dropdown/DropdownMenuSeparator.tsx @@ -1,5 +1,5 @@ import { FC, PropsWithChildren } from "react"; export const DropdownMenuSeparator: FC = ({ children }) => { - return
{children}
; + return
{children}
; }; diff --git a/packages/ui/src/components/Dropdown/index.tsx b/packages/ui/src/components/Dropdown/index.tsx index 137f60f88f..011e3b3399 100644 --- a/packages/ui/src/components/Dropdown/index.tsx +++ b/packages/ui/src/components/Dropdown/index.tsx @@ -78,7 +78,7 @@ export const Dropdown: FC = ({
= ({ children }) => {
{children}
{description !== undefined && ( -
{description}
+
{description}
)} ); diff --git a/packages/ui/src/forms/fields/SelectFormField.tsx b/packages/ui/src/forms/fields/SelectFormField.tsx index 2b137b8269..6496b43fe9 100644 --- a/packages/ui/src/forms/fields/SelectFormField.tsx +++ b/packages/ui/src/forms/fields/SelectFormField.tsx @@ -204,29 +204,28 @@ export function SelectFormField< * But it would require @emotion/cache dependency and also I'm unsure about performance implications. */ "!min-h-0", - "bg-white border-slate-300 border rounded-md shadow-sm focus-within:ring-indigo-500 focus-within:border-indigo-500 focus-within:ring-1" + "bg-white border-gray-300 border rounded-sm shadow-sm focus-within:ring-blue-500 focus-within:border-blue-500 focus-within:ring-1" ), // disable default browser focus style input: () => "[&_input:focus]:!ring-transparent", placeholder: () => - clsx("text-slate-300", size === "small" && "text-sm"), + clsx("text-gray-300", size === "small" && "text-sm"), valueContainer: () => "px-3", - clearIndicator: () => - "text-slate-300 hover:text-slate-500 px-2", + clearIndicator: () => "text-gray-300 hover:text-gray-500 px-2", loadingIndicator: () => - "text-slate-300 hover:text-slate-500 px-2", - indicatorSeparator: () => "w-px bg-slate-300 my-2", + "text-gray-300 hover:text-gray-500 px-2", + indicatorSeparator: () => "w-px bg-gray-300 my-2", dropdownIndicator: () => - "text-slate-300 hover:text-slate-500 px-2", + "text-gray-300 hover:text-gray-500 px-2", menuPortal: () => "!z-[100]", // based on Dropdown styles menu: () => - "mt-2 rounded-md bg-white shadow-xl border border-slate-300 overflow-hidden", + "mt-2 rounded-sm bg-white shadow-xl border border-gray-300 overflow-hidden", menuList: () => "p-1 overflow-auto", option: () => - "px-2 py-1.5 rounded hover:bg-blue-100 text-slate-700 hover:text-slate-900", - loadingMessage: () => "text-slate-500", - noOptionsMessage: () => "text-slate-400 p-2", + "px-2 py-1.5 rounded hover:bg-slate-100 text-gray-700 hover:text-gray-700", + loadingMessage: () => "text-gray-500", + noOptionsMessage: () => "text-gray-400 p-2", }} menuPortalTarget={ typeof document === "undefined" ? undefined : document.body diff --git a/packages/ui/src/forms/styled/StyledCheckbox.tsx b/packages/ui/src/forms/styled/StyledCheckbox.tsx index 9cf1716a18..013f0f04f4 100644 --- a/packages/ui/src/forms/styled/StyledCheckbox.tsx +++ b/packages/ui/src/forms/styled/StyledCheckbox.tsx @@ -9,7 +9,7 @@ export const StyledCheckbox = forwardRef< type="checkbox" ref={ref} {...props} - className="form-checkbox h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" + className="form-checkbox h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" /> ); }); diff --git a/packages/ui/src/forms/styled/StyledColorInput.tsx b/packages/ui/src/forms/styled/StyledColorInput.tsx index 018cc74cad..2b008b3a1e 100644 --- a/packages/ui/src/forms/styled/StyledColorInput.tsx +++ b/packages/ui/src/forms/styled/StyledColorInput.tsx @@ -20,13 +20,13 @@ export function StyledColorInput({ value, onChange }: Props) { color={value} onChange={onChange} prefixed - className="mt-1 w-20 border border-gray-300 bg-slate-100 px-1 py-0 text-sm outline-indigo-500" + className="mt-1 w-20 border border-gray-300 bg-gray-100 px-1 py-0 text-sm outline-blue-500" />
)} > -
+
( type={type} {...props} className={clsx( - "form-input block w-full rounded-md border-slate-300 text-sm shadow-sm placeholder:text-slate-300 focus:border-indigo-500 focus:ring-indigo-500 active:border-indigo-500 active:ring-indigo-500 disabled:text-slate-400", + "form-input block w-full rounded-sm border-gray-300 text-sm shadow-sm placeholder:text-gray-300 focus:border-blue-500 focus:ring-blue-500 active:border-blue-500 active:ring-blue-500 disabled:text-gray-400", size === "normal" ? "h-10" : "h-8" )} /> diff --git a/packages/ui/src/forms/styled/StyledRadio.tsx b/packages/ui/src/forms/styled/StyledRadio.tsx index 6dbc9add2e..b0492b45cd 100644 --- a/packages/ui/src/forms/styled/StyledRadio.tsx +++ b/packages/ui/src/forms/styled/StyledRadio.tsx @@ -28,7 +28,7 @@ export function StyledRadio({ value, options, onChange }: StyledRadioProps) { onChange={() => onChange(option.id)} checked={option.id === value} className={clsx( - "form-radio text-indigo-500 focus:ring-transparent", + "form-radio text-blue-500 focus:ring-transparent", option.disabled ? "cursor-not-allowed" : "cursor-pointer" )} disabled={option.disabled} @@ -40,7 +40,7 @@ export function StyledRadio({ value, options, onChange }: StyledRadioProps) { "text-sm font-medium", option.disabled ? "cursor-not-allowed text-gray-400" - : "cursor-pointer text-gray-600 group-hover:text-gray-900" + : "cursor-pointer text-gray-600 group-hover:text-gray-800" )} > {option.name} diff --git a/packages/ui/src/forms/styled/StyledTextArea.tsx b/packages/ui/src/forms/styled/StyledTextArea.tsx index 6c21be99ab..80514c257a 100644 --- a/packages/ui/src/forms/styled/StyledTextArea.tsx +++ b/packages/ui/src/forms/styled/StyledTextArea.tsx @@ -16,8 +16,8 @@ export const StyledTextArea = forwardRef< ref={ref} {...props} className={clsx( - "form-input block w-full rounded-md border-slate-300 text-sm shadow-sm placeholder:text-slate-300 focus:border-indigo-500 focus:ring-indigo-500 active:border-indigo-500 active:ring-indigo-500" - // disabled && "text-slate-400" + "form-input block w-full rounded-sm border-gray-300 text-sm shadow-sm placeholder:text-gray-300 focus:border-blue-500 focus:ring-blue-500 active:border-blue-500 active:ring-blue-500" + // disabled && "text-gray-400" )} /> ); diff --git a/packages/ui/src/forms/styled/StyledToggle.tsx b/packages/ui/src/forms/styled/StyledToggle.tsx new file mode 100644 index 0000000000..3d18973e6d --- /dev/null +++ b/packages/ui/src/forms/styled/StyledToggle.tsx @@ -0,0 +1,52 @@ +import { Switch } from "@headlessui/react"; +import clsx from "clsx"; + +export type StyledToggleProps = { + checked: boolean; + showFocusRing?: boolean; + size?: "medium" | "tiny"; + onChange(newValue: boolean): void; +}; + +export function StyledToggle({ + checked, + showFocusRing = true, + size = "medium", + onChange, +}: StyledToggleProps) { + const sizeClasses = { + medium: { + toggle: "h-6 w-11", + circle: "h-5 w-5", + translate: "translate-x-5", + }, + tiny: { + toggle: "h-3 w-5", + circle: "h-2 w-2", + translate: "translate-x-2", + }, + }; + + const { toggle, circle, translate } = sizeClasses[size]; + + return ( + + Toggle + + ); +} diff --git a/packages/ui/src/icons/HeroIcons.tsx b/packages/ui/src/icons/HeroIcons.tsx index d2efb02a19..9bc2a5348a 100644 --- a/packages/ui/src/icons/HeroIcons.tsx +++ b/packages/ui/src/icons/HeroIcons.tsx @@ -165,21 +165,33 @@ export const LinkIcon: FC = (props) => ( ); export const ChevronRightIcon: FC = (props) => ( - + ); export const ChevronLeftIcon: FC = (props) => ( - + ); diff --git a/packages/ui/src/icons/Icon.tsx b/packages/ui/src/icons/Icon.tsx index 5014a2698c..2c035f7b68 100644 --- a/packages/ui/src/icons/Icon.tsx +++ b/packages/ui/src/icons/Icon.tsx @@ -4,6 +4,11 @@ export type IconProps = PropsWithChildren<{ size?: number; className?: string; onClick?: () => void; + strokeWidth?: number; + stroke?: string; + strokeLinecap?: "butt" | "round" | "square"; + strokeLinejoin?: "miter" | "round" | "bevel"; + fill?: string; }>; export const Icon: FC = ({ @@ -11,15 +16,24 @@ export const Icon: FC = ({ className, onClick, viewBox = "0 0 20 20", + strokeWidth, + stroke, + strokeLinecap, + strokeLinejoin, + fill = "currentColor", children, }) => ( {children} diff --git a/packages/ui/src/icons/IconByName.tsx b/packages/ui/src/icons/IconByName.tsx new file mode 100644 index 0000000000..4872075a7d --- /dev/null +++ b/packages/ui/src/icons/IconByName.tsx @@ -0,0 +1,153 @@ +import { FC } from "react"; + +import { + Die1Icon, + Die2Icon, + Die3Icon, + Die4Icon, + Die5Icon, + Die6Icon, +} from "./DieIcons.js"; +import { DotsHorizontalIcon } from "./DotsHorizontalIcon.js"; +import { EditIcon } from "./EditIcon.js"; +import { EmptyIcon } from "./EmptyIcon.js"; +import { ErrorIcon } from "./ErrorIcon.js"; +import { ExternalLinkIcon } from "./ExternalLinkIcon.js"; +import { FocusIcon } from "./FocusIcon.js"; +import { + AdjustmentsHorizontalIcon, + AdjustmentsVerticalIcon, + ArchiveBoxIcon, + BackwardIcon, + BarChartIcon, + Bars3CenterLeftIcon, + Bars4Icon, + BoltIcon, + BookOpenIcon, + CalculatorIcon, + CheckIcon, + ChevronLeftIcon, + ChevronRightIcon, + ClipboardCopyIcon, + CodeBracketIcon, + CodeBracketSquareIcon, + Cog8ToothIcon, + CommandLineIcon, + CommentIcon, + CubeTransparentIcon, + CurlyBracketsIcon, + DocumentTextIcon, + FireIcon, + GlobeIcon, + GroupIcon, + HashIcon, + HelpIcon, + LinkIcon, + ListBulletIcon, + LockIcon, + PauseIcon, + PlayIcon, + PuzzleIcon, + RectangleStackIcon, + ResetIcon, + RightArrowIcon, + ScaleIcon, + ScatterPlotIcon, + SearchIcon, + ShareIcon, + SquareBracketIcon, + TableCellsIcon, + UserCircleIcon, + UserIcon, + VariableIcon, + WrenchIcon, +} from "./HeroIcons.js"; +import type { IconProps } from "./Icon.js"; +import { PlusIcon } from "./PlusIcon.js"; +import { RefreshIcon } from "./RefreshIcon.js"; +import { SignOutIcon } from "./SignOutIcon.js"; +import { TrashIcon } from "./TrashIcon.js"; +import { TriangleIcon } from "./TriangleIcon.js"; +import { XIcon } from "./XIcon.js"; + +export const allIconsMap: Record | null> = { + AdjustmentsHorizontalIcon, + AdjustmentsVerticalIcon, + ArchiveBoxIcon, + BackwardIcon, + BarChartIcon, + Bars3CenterLeftIcon, + Bars4Icon, + BoltIcon, + BookOpenIcon, + CalculatorIcon, + CheckIcon, + ChevronLeftIcon, + ChevronRightIcon, + ClipboardCopyIcon, + CodeBracketIcon, + CodeBracketSquareIcon, + Cog8ToothIcon, + CommandLineIcon, + CommentIcon, + CubeTransparentIcon, + CurlyBracketsIcon, + DocumentTextIcon, + DotsHorizontalIcon, + EditIcon, + EmptyIcon, + ErrorIcon, + ExternalLinkIcon, + FireIcon, + FocusIcon, + GlobeIcon, + GroupIcon, + HashIcon, + HelpIcon, + LinkIcon, + ListBulletIcon, + LockIcon, + PauseIcon, + PlayIcon, + PlusIcon, + PuzzleIcon, + RectangleStackIcon, + RefreshIcon, + ResetIcon, + RightArrowIcon, + ScaleIcon, + ScatterPlotIcon, + SearchIcon, + ShareIcon, + SignOutIcon, + SquareBracketIcon, + TableCellsIcon, + TrashIcon, + TriangleIcon, + UserCircleIcon, + UserIcon, + VariableIcon, + WrenchIcon, + XIcon, + Die1Icon, + Die2Icon, + Die3Icon, + Die4Icon, + Die5Icon, + Die6Icon, +}; + +const findByName = (name: string) => allIconsMap[name] || null; + +export const IconByName: FC = ({ + name, + ...props +}) => { + const IconComponent = findByName(name); + + if (!IconComponent) { + return null; + } + + return ; +}; diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index ecbe4acdee..11ec3103fb 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -38,6 +38,7 @@ export { StyledCheckbox } from "./forms/styled/StyledCheckbox.js"; export { StyledColorInput } from "./forms/styled/StyledColorInput.js"; export { StyledInput } from "./forms/styled/StyledInput.js"; export { StyledTextArea } from "./forms/styled/StyledTextArea.js"; +// export { StyledToggle } from "./forms/styled/StyledToggle.js"; // For some reason, this breaks the Hub, in dev. export { DotsHorizontalIcon } from "./icons/DotsHorizontalIcon.js"; export { EditIcon } from "./icons/EditIcon.js"; @@ -108,6 +109,7 @@ export { Die6Icon, } from "./icons/DieIcons.js"; export { ErrorIcon } from "./icons/ErrorIcon.js"; +export { IconByName } from "./icons/IconByName.js"; export { useToast, WithToasts } from "./components/WithToasts/index.js"; diff --git a/packages/ui/src/stories/StyledTab.stories.tsx b/packages/ui/src/stories/Forms/StyledTab.stories.tsx similarity index 87% rename from packages/ui/src/stories/StyledTab.stories.tsx rename to packages/ui/src/stories/Forms/StyledTab.stories.tsx index f7cab6e3ba..1a2df0fd45 100644 --- a/packages/ui/src/stories/StyledTab.stories.tsx +++ b/packages/ui/src/stories/Forms/StyledTab.stories.tsx @@ -1,7 +1,7 @@ import type { Meta, StoryObj } from "@storybook/react"; -import { StyledTab } from "../components/StyledTab.js"; -import { BoltIcon, FireIcon } from "../index.js"; +import { StyledTab } from "../../components/StyledTab.js"; +import { BoltIcon, FireIcon } from "../../icons/HeroIcons.js"; /** * StyledTab component wraps the [\](https://headlessui.com/react/tabs) component from Headless UI. @@ -21,7 +21,7 @@ export const Default: Story = { -
+
Code panel Settings panel diff --git a/packages/ui/src/stories/Forms/StyledToggle.stories.tsx b/packages/ui/src/stories/Forms/StyledToggle.stories.tsx new file mode 100644 index 0000000000..3d04c8e700 --- /dev/null +++ b/packages/ui/src/stories/Forms/StyledToggle.stories.tsx @@ -0,0 +1,51 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import { useState } from "react"; + +import { + StyledToggle, + StyledToggleProps, +} from "../../forms/styled/StyledToggle.js"; + +const meta = { component: StyledToggle } satisfies Meta; + +export default meta; + +type Story = StoryObj; + +const renderToggle = (args: StyledToggleProps) => { + const [checked, setChecked] = useState(false); + return ; +}; + +export const Default: Story = { + args: { + checked: false, + onChange: (newValue: boolean) => { + console.log("Toggle value changed to:", newValue); + }, + }, + + render: (args) => renderToggle(args), +}; + +export const WithoutFocusRing: Story = { + args: { + checked: false, + showFocusRing: false, + onChange: (newValue: boolean) => { + console.log("Toggle value changed to:", newValue); + }, + }, + render: (args) => renderToggle(args), +}; + +export const Tiny: Story = { + args: { + checked: false, + size: "tiny", + onChange: (newValue: boolean) => { + console.log("Toggle value changed to:", newValue); + }, + }, + render: (args) => renderToggle(args), +}; diff --git a/packages/ui/src/stories/Icons/IconByName.stories.tsx b/packages/ui/src/stories/Icons/IconByName.stories.tsx new file mode 100644 index 0000000000..9df946861d --- /dev/null +++ b/packages/ui/src/stories/Icons/IconByName.stories.tsx @@ -0,0 +1,40 @@ +import type { Meta, StoryObj } from "@storybook/react"; + +import { allIconsMap, IconByName } from "../../icons/IconByName.js"; + +const meta = { component: IconByName } satisfies Meta; +export default meta; +type Story = StoryObj; + +export const AllIcons: Story = { + args: { + name: "DocumentTextIcon", + }, + render: ({ size }) => ( +
+ {Object.keys(allIconsMap).map((name) => ( +
+
+ +
+
{name}
+
+ ))} +
+ ), +}; diff --git a/packages/versioned-components/src/SquigglePlaygroundVersionPicker.tsx b/packages/versioned-components/src/SquigglePlaygroundVersionPicker.tsx deleted file mode 100644 index 7e51980e42..0000000000 --- a/packages/versioned-components/src/SquigglePlaygroundVersionPicker.tsx +++ /dev/null @@ -1,100 +0,0 @@ -"use client"; -import { FC } from "react"; - -import { - Button, - Dropdown, - DropdownMenu, - DropdownMenuActionItem, - DropdownMenuHeader, - DropdownMenuLinkItem, - DropdownMenuModalActionItem, - DropdownMenuSeparator, - HelpIcon, - Modal, -} from "@quri/ui"; - -import { - SquiggleVersionShower, - versionIcon, - versionTitle, -} from "./SquiggleVersionShower.js"; -import { SquiggleVersion, squiggleVersions } from "./versions.js"; - -const CHANGELOG_URL = "https://www.squiggle-language.com/docs/Changelog"; - -export const SquigglePlaygroundVersionPicker: FC<{ - version: string; - onChange: (newVersion: SquiggleVersion) => void; - // This is mostly Squiggle Hub specific, but we might later decide to do auto-updates in Squiggle Playground too. - showUpdatePolicy?: boolean; - // "medium" was intended for "New Model" form in Squiggle Hub, but it's currently unused - size: "small" | "medium"; -}> = ({ version, onChange, showUpdatePolicy, size }) => { - return ( -
- ( - - Squiggle Version - {squiggleVersions.map((version) => ( - { - onChange(version); - close(); - }} - /> - ))} - - {showUpdatePolicy ? ( - ( - - -
- - Version Update Policy -
-
- - {/* Markdown here would be nice, but we don't have it available in @quri/ui. */} -
-

- We’ll auto-update your model to the most recent - non-breaking version of Squiggle. -

-

- However, if you use the{" "} - Next version, - it will always stay on the Beta - so only do this if - you’re okay fixing it if it breaks, or temporarily if - you need to use a feature that’s not been released - yet. -

-
-
-
- )} - /> - ) : null} - -
- )} - > - -
-
- ); -}; diff --git a/packages/versioned-components/src/SquigglePlaygroundVersionPickerDropdown.tsx b/packages/versioned-components/src/SquigglePlaygroundVersionPickerDropdown.tsx new file mode 100644 index 0000000000..7eb89d35d9 --- /dev/null +++ b/packages/versioned-components/src/SquigglePlaygroundVersionPickerDropdown.tsx @@ -0,0 +1,112 @@ +"use client"; +import { FC, PropsWithChildren } from "react"; + +import { + CodeBracketIcon, + Dropdown, + DropdownMenu, + DropdownMenuActionItem, + DropdownMenuHeader, + DropdownMenuLinkItem, + DropdownMenuModalActionItem, + DropdownMenuSeparator, + HelpIcon, + Modal, + WrenchIcon, +} from "@quri/ui"; + +import { + checkSquiggleVersion, + SquiggleVersion, + squiggleVersions, +} from "./versions.js"; + +export function versionTitle(version: SquiggleVersion) { + return version === "dev" ? "next" : `v${version}`; +} + +export function uncheckedVersionTitle(version: string) { + const versionIsValid = checkSquiggleVersion(version); + if (versionIsValid) { + return versionTitle(version); + } else { + return `${version} (unknown)`; + } +} + +export function versionIcon(version: string) { + return version === "dev" ? WrenchIcon : CodeBracketIcon; +} + +const CHANGELOG_URL = "https://www.squiggle-language.com/docs/Changelog"; + +export const SquigglePlaygroundVersionPickerDropdown: FC< + PropsWithChildren<{ + onChange: (newVersion: SquiggleVersion) => void; + // This is mostly Squiggle Hub specific, but we might later decide to do auto-updates in Squiggle Playground too. + showUpdatePolicy?: boolean; + }> +> = ({ onChange, showUpdatePolicy, children }) => { + return ( + ( + + Squiggle Version + {squiggleVersions.map((version) => ( + { + onChange(version); + close(); + }} + /> + ))} + + {showUpdatePolicy ? ( + ( + + +
+ + Version Update Policy +
+
+ + {/* Markdown here would be nice, but we don't have it available in @quri/ui. */} +
+

+ We’ll auto-update your model to the most recent + non-breaking version of Squiggle. +

+

+ However, if you use the{" "} + Next version, it + will always stay on the Beta - so only do this if you’re + okay fixing it if it breaks, or temporarily if you need + to use a feature that’s not been released yet. +

+
+
+
+ )} + /> + ) : null} + +
+ )} + > + {children} +
+ ); +}; diff --git a/packages/versioned-components/src/SquiggleVersionShower.tsx b/packages/versioned-components/src/SquiggleVersionShower.tsx deleted file mode 100644 index 7e6e33e473..0000000000 --- a/packages/versioned-components/src/SquiggleVersionShower.tsx +++ /dev/null @@ -1,36 +0,0 @@ -"use client"; -import { FC } from "react"; - -import { CodeBracketIcon, WrenchIcon } from "@quri/ui"; - -import { checkSquiggleVersion, SquiggleVersion } from "./versions.js"; - -export function versionTitle(version: SquiggleVersion) { - return version === "dev" ? "Next" : version; -} - -export function uncheckedVersionTitle(version: string) { - const versionIsValid = checkSquiggleVersion(version); - if (versionIsValid) { - return versionTitle(version); - } else { - return `${version} (unknown)`; - } -} - -export function versionIcon(version: string) { - return version === "dev" ? WrenchIcon : CodeBracketIcon; -} - -export const SquiggleVersionShower: FC<{ version: string }> = ({ version }) => { - const CurrentIcon = versionIcon(version); - - return ( -
- -
- {uncheckedVersionTitle(version)} -
-
- ); -}; diff --git a/packages/versioned-components/src/index.ts b/packages/versioned-components/src/index.ts index a53155908c..ef9c73930f 100644 --- a/packages/versioned-components/src/index.ts +++ b/packages/versioned-components/src/index.ts @@ -1,5 +1,7 @@ -export { SquigglePlaygroundVersionPicker } from "./SquigglePlaygroundVersionPicker.js"; -export { SquiggleVersionShower } from "./SquiggleVersionShower.js"; +export { + SquigglePlaygroundVersionPickerDropdown, + uncheckedVersionTitle, +} from "./SquigglePlaygroundVersionPickerDropdown.js"; export { versionedSquigglePackages } from "./versionedSquigglePackages.js"; export { diff --git a/packages/website/src/components/PlaygroundPage/index.tsx b/packages/website/src/components/PlaygroundPage/index.tsx index aa68e91f3d..49255ce14b 100644 --- a/packages/website/src/components/PlaygroundPage/index.tsx +++ b/packages/website/src/components/PlaygroundPage/index.tsx @@ -2,11 +2,13 @@ import { fromByteArray, toByteArray } from "base64-js"; import { deflate, inflate } from "pako"; import { FC, useEffect, useState } from "react"; +import { PlaygroundToolbarItem } from "@quri/squiggle-components"; import { defaultSquiggleVersion, - SquigglePlaygroundVersionPicker, + SquigglePlaygroundVersionPickerDropdown, type SquiggleVersion, squiggleVersions, + uncheckedVersionTitle, versionedSquigglePackages, } from "@quri/versioned-squiggle-components"; @@ -112,11 +114,14 @@ export const PlaygroundPage: FC<{ version: string | null }> = (props) => { renderExtraControls={() => (
- + showUpdatePolicy + > + + {uncheckedVersionTitle(version)} + +
)} onCodeChange={(code) => updateUrl({ defaultCode: code }, version)}