From 37e6c8342f1f63a95918a71ac78130b9d10d1103 Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Tue, 14 Oct 2025 07:14:26 -0500 Subject: wip: css and ui packages --- packages/ui/src/app.tsx | 68 +++++++++++++++ packages/ui/src/components/button.tsx | 25 ++++++ packages/ui/src/components/icon.tsx | 155 ++++++++++++++++++++++++++++++++++ packages/ui/src/components/index.ts | 3 + packages/ui/src/components/select.tsx | 92 ++++++++++++++++++++ packages/ui/src/components/style.css | 2 + packages/ui/src/index.css | 39 +++++++++ packages/ui/src/index.tsx | 15 ++++ 8 files changed, 399 insertions(+) create mode 100644 packages/ui/src/app.tsx create mode 100644 packages/ui/src/components/button.tsx create mode 100644 packages/ui/src/components/icon.tsx create mode 100644 packages/ui/src/components/index.ts create mode 100644 packages/ui/src/components/select.tsx create mode 100644 packages/ui/src/components/style.css create mode 100644 packages/ui/src/index.css create mode 100644 packages/ui/src/index.tsx (limited to 'packages/ui/src') diff --git a/packages/ui/src/app.tsx b/packages/ui/src/app.tsx new file mode 100644 index 000000000..be2ec357e --- /dev/null +++ b/packages/ui/src/app.tsx @@ -0,0 +1,68 @@ +import type { Component } from "solid-js" +import { Button } from "./components/button" +import { Select } from "./components" +import "@opencode-ai/css" +import "./index.css" + +const App: Component = () => { + return ( +
+
+

Buttons

+
+ + + + + + +
+

Select

+
+ console.log(x)} placeholder="Select" /> +
+
+
+ ) +} + +export default App diff --git a/packages/ui/src/components/button.tsx b/packages/ui/src/components/button.tsx new file mode 100644 index 000000000..db1da2fb9 --- /dev/null +++ b/packages/ui/src/components/button.tsx @@ -0,0 +1,25 @@ +import { Button as Kobalte } from "@kobalte/core/button" +import { type ComponentProps, splitProps } from "solid-js" + +export interface ButtonProps { + size?: "normal" | "large" + variant?: "primary" | "secondary" | "ghost" +} + +export function Button(props: ComponentProps<"button"> & ButtonProps) { + const [split, rest] = splitProps(props, ["variant", "size", "class", "classList"]) + return ( + + {props.children} + + ) +} diff --git a/packages/ui/src/components/icon.tsx b/packages/ui/src/components/icon.tsx new file mode 100644 index 000000000..05dda6ea6 --- /dev/null +++ b/packages/ui/src/components/icon.tsx @@ -0,0 +1,155 @@ +import { splitProps, type ComponentProps } from "solid-js" + +// prettier-ignore +const icons = { + close: '', + menu: ' ', + "chevron-right": '', + "chevron-left": '', + "chevron-down": '', + "chevron-up": '', + "chevron-down-square": '', + "chevron-up-square": '', + "chevron-right-square": '', + "chevron-left-square": '', + settings: '', + globe: '', + github: '', + hammer: '', + "avatar-square": '', + slash: '', + robot: '', + cloud: '', + "file-text": '', + file: '', + "file-checkmark": '', + "file-code": '', + "file-important": '', + "file-minus": '', + "file-plus": '', + files: '', + "file-zip": '', + jpg: '', + pdf: '', + png: '', + gif: '', + archive: '', + sun: '', + moon: '', + monitor: '', + command: '', + link: '', + share: '', + branch: '', + logout: '', + login: '', + keys: '', + key: '', + info: '', + warning: '', + checkmark: '', + "checkmark-square": '', + plus: '', + minus: '', + undo: '', + merge: '', + redo: '', + refresh: '', + rotate: '', + "arrow-left": '', + "arrow-down": '', + "arrow-right": '', + "arrow-up": '', + enter: '', + trash: '', + package: '', + box: '', + lock: '', + unlocked: '', + activity: '', + asterisk: '', + bell: '', + "bell-off": '', + bolt: '', + bookmark: '', + brain: '', + browser: '', + "browser-cursor": '', + bug: '', + "carat-down": '', + "carat-left": '', + "carat-right": '', + "carat-up": '', + cards: '', + chart: '', + "check-circle": '', + checklist: '', + "checklist-cards": '', + lab: '', + circle: '', + "circle-dotted": '', + clipboard: '', + clock: '', + "close-circle": '', + terminal: '', + code: '', + components: '', + copy: '', + cpu: '', + dashboard: '', + transfer: '', + devices: '', + diamond: '', + dice: '', + discord: '', + dots: '', + expand: '', + droplet: '', + "chevron-double-down": '', + "chevron-double-left": '', + "chevron-double-right": '', + "chevron-double-up": '', + "speech-bubble": '', + message: '', + annotation: '', + square: '', + "pull-request": '', + pencil: '', + sparkles: '', + photo: '', + columns: '', + "open-pane": '', + "close-pane": '', + "file-search": '', + "folder-search": '', + search: '', + "web-search": '', + loading: '', + mic: '', +} as const + +export interface IconProps extends ComponentProps<"svg"> { + name: keyof typeof icons + size?: number +} + +export function Icon(props: IconProps) { + const [local, others] = splitProps(props, ["name", "size", "class", "classList"]) + const size = local.size ?? 24 + return ( + + ) +} diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts new file mode 100644 index 000000000..80d80299b --- /dev/null +++ b/packages/ui/src/components/index.ts @@ -0,0 +1,3 @@ +export * from "./button" +export * from "./icon" +export * from "./select" diff --git a/packages/ui/src/components/select.tsx b/packages/ui/src/components/select.tsx new file mode 100644 index 000000000..c4d5443c1 --- /dev/null +++ b/packages/ui/src/components/select.tsx @@ -0,0 +1,92 @@ +import { Select as Kobalte } from "@kobalte/core/select" +import { createMemo, type ComponentProps } from "solid-js" +import { Icon, Button, type ButtonProps } from "@opencode-ai/ui" +import { pipe, groupBy, entries, map } from "remeda" + +export interface SelectProps { + placeholder?: string + options: T[] + current?: T + value?: (x: T) => string + label?: (x: T) => string + groupBy?: (x: T) => string + onSelect?: (value: T | undefined) => void + class?: ComponentProps<"div">["class"] + classList?: ComponentProps<"div">["classList"] +} + +export function Select(props: SelectProps & ButtonProps) { + const grouped = createMemo(() => { + const result = pipe( + props.options, + groupBy((x) => (props.groupBy ? props.groupBy(x) : "")), + // mapValues((x) => x.sort((a, b) => a.title.localeCompare(b.title))), + entries(), + map(([k, v]) => ({ category: k, options: v })), + ) + return result + }) + + return ( + + data-component="select" + value={props.current} + options={grouped()} + optionValue={(x) => (props.value ? props.value(x) : (x as string))} + optionTextValue={(x) => (props.label ? props.label(x) : (x as string))} + optionGroupChildren="options" + placeholder={props.placeholder} + sectionComponent={(props) => ( + {props.section.rawValue.category} + )} + itemComponent={(itemProps) => ( + + + {props.label ? props.label(itemProps.item.rawValue) : (itemProps.item.rawValue as string)} + + + + + + )} + onChange={(v) => { + props.onSelect?.(v ?? undefined) + }} + > + + data-slot="value"> + {(state) => { + const selected = state.selectedOption() ?? props.current + if (!selected) return props.placeholder || "" + if (props.label) return props.label(selected) + return selected as string + }} + + + + + + + + + + + + ) +} diff --git a/packages/ui/src/components/style.css b/packages/ui/src/components/style.css new file mode 100644 index 000000000..52539dc8f --- /dev/null +++ b/packages/ui/src/components/style.css @@ -0,0 +1,2 @@ +/* re-exporting for convenience */ +@import "@opencode-ai/css"; diff --git a/packages/ui/src/index.css b/packages/ui/src/index.css new file mode 100644 index 000000000..be7adaaed --- /dev/null +++ b/packages/ui/src/index.css @@ -0,0 +1,39 @@ +:root { + body { + margin: 0; + background-color: var(--background-background); + color: var(--text-default-text); + } + main { + display: flex; + flex-direction: row; + height: 100vh; + overflow-x: hidden; + } + main > div { + flex: 1; + padding: 3rem; + min-width: 0; + overflow-y: auto; + overflow-x: hidden; + display: flex; + flex-direction: column; + gap: 2rem; + } + h3 { + font-size: 1.25rem; + font-weight: 600; + margin: 0 0 1rem 0; + } + section { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + align-items: flex-start; + } +} + +.dark { + background-color: var(--background-background); + color: var(--text-default-text); +} diff --git a/packages/ui/src/index.tsx b/packages/ui/src/index.tsx new file mode 100644 index 000000000..216765711 --- /dev/null +++ b/packages/ui/src/index.tsx @@ -0,0 +1,15 @@ +/* @refresh reload */ +import { render } from "solid-js/web" +import "solid-devtools" + +import App from "./app" + +const root = document.getElementById("root") + +if (import.meta.env.DEV && !(root instanceof HTMLElement)) { + throw new Error( + "Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got misspelled?", + ) +} + +render(() => , root!) -- cgit v1.2.3