From f1745b8a1b23962ef30f33fe52e0fd9fe6cca4fd Mon Sep 17 00:00:00 2001 From: Max Amundsen Date: Wed, 15 Jul 2026 11:20:47 -0400 Subject: [PATCH] update from cdrl --- go/jsruntime/uikit/AutoTable.tsx | 28 +- go/jsruntime/uikit/Buttons.tsx | 29 +- go/jsruntime/uikit/DatePicker.tsx | 7 +- go/jsruntime/uikit/Forms.tsx | 434 ++++++++++-------------------- go/jsruntime/uikit/General.tsx | 18 +- go/jsruntime/uikit/Icons.tsx | 28 +- go/jsruntime/uikit/Sidebar.tsx | 2 +- go/jsruntime/uikit/Validation.ts | 2 +- 8 files changed, 195 insertions(+), 353 deletions(-) diff --git a/go/jsruntime/uikit/AutoTable.tsx b/go/jsruntime/uikit/AutoTable.tsx index 45ac057e..0640cfa9 100644 --- a/go/jsruntime/uikit/AutoTable.tsx +++ b/go/jsruntime/uikit/AutoTable.tsx @@ -324,12 +324,12 @@ export interface AutoTablePagination { CurrentPage: number; NextPage?: number; PreviousPage?: number; - TotalPages: number; - TotalItems: number; + TotalPages?: number; + TotalItems?: number; MaxItemsPerPage: number; ItemsThisPage?: number; - ViewRangeLower: number; - ViewRangeUpper: number; + ViewRangeLower?: number; + ViewRangeUpper?: number; } export interface AutoTableOrderBy { @@ -449,7 +449,7 @@ export function AutoTableDateSearch(props: AutoTableDateSearchProps) { @@ -4181,33 +4181,27 @@ function PaginationButton(props: JSX.ButtonHTMLAttributes) { ; } -interface TdProps { - class?: string; - style?: string; - children: any; -} - -export function TdLeft(props: TdProps) { +export function TdLeft(props: JSX.TdHTMLAttributes) { return {props.children}; } -export function TdRight(props: TdProps) { +export function TdRight(props: JSX.TdHTMLAttributes) { return {props.children}; } -export function TdCenter(props: TdProps) { +export function TdCenter(props: JSX.TdHTMLAttributes) { return {props.children}; } -export function TdUniformLeft(props: TdProps) { +export function TdUniformLeft(props: JSX.TdHTMLAttributes) { return {props.children}; } -export function TdUniformRight(props: TdProps) { +export function TdUniformRight(props: JSX.TdHTMLAttributes) { return {props.children}; } -export function TdUniformCenter(props: TdProps) { +export function TdUniformCenter(props: JSX.TdHTMLAttributes) { return {props.children}; } diff --git a/go/jsruntime/uikit/Buttons.tsx b/go/jsruntime/uikit/Buttons.tsx index e9c23a0c..98b8ed15 100644 --- a/go/jsruntime/uikit/Buttons.tsx +++ b/go/jsruntime/uikit/Buttons.tsx @@ -1,4 +1,4 @@ -import { JSX, JSXElement } from "solid-js"; +import { JSX, JSXElement, splitProps } from "solid-js"; import { Icon } from "./Icons.tsx"; export const BUTTON_COLOR_NEUTRAL = "neutral"; @@ -63,37 +63,36 @@ interface ButtonUIProps extends JSX.ButtonHTMLAttributes { } export function ButtonUI(props: ButtonUIProps) { - const hasText = () => props.text !== undefined ? !!props.text : !props.icon; + const [local, buttonProps] = splitProps(props, ["text", "icon", "outline", "color", "small", "class", "type", "children"]); + const hasText = () => local.text !== undefined ? !!local.text : !local.icon; const cls = () => { let c = BASE; - if (props.outline) { - c += " " + OUTLINE_BASE + " " + (OUTLINE_COLORS[props.color!] || OUTLINE_COLORS["neutral"]); + if (local.outline) { + c += " " + OUTLINE_BASE + " " + (OUTLINE_COLORS[local.color!] || OUTLINE_COLORS["neutral"]); } else { - c += " " + (COLORS[props.color!] || COLORS["neutral"]); + c += " " + (COLORS[local.color!] || COLORS["neutral"]); } - if (props.small) { - c += props.icon ? " py-1 px-3" : " py-1 px-4"; - } else if (props.icon && !hasText()) { + if (local.small) { + c += local.icon ? " py-1 px-3" : " py-1 px-4"; + } else if (local.icon && !hasText()) { c += " py-2 px-3"; - } else if (props.icon) { + } else if (local.icon) { c += " py-2 px-5"; } else { c += " py-2 px-8"; } + if (local.class) c += " " + local.class; return c; }; return ( ); } diff --git a/go/jsruntime/uikit/DatePicker.tsx b/go/jsruntime/uikit/DatePicker.tsx index ebeb5a99..bbbc9459 100644 --- a/go/jsruntime/uikit/DatePicker.tsx +++ b/go/jsruntime/uikit/DatePicker.tsx @@ -1,4 +1,4 @@ -import { createSignal, createEffect, Show, onMount, onCleanup, For } from "solid-js"; +import { createSignal, createEffect, Show, onMount, onCleanup, For, JSX } from "solid-js"; import { Portal } from "solid-js/web"; import { Icon } from "./Icons.tsx"; import { FormInput } from "./Forms.tsx"; @@ -179,7 +179,10 @@ const DATE_PICKER_ICON_BTN = "absolute inset-y-0 right-0 z-[1] flex items-center const DATE_PICKER_CLEAR_BTN = "absolute inset-y-0 right-8 z-[1] flex items-center justify-center bg-transparent border-0 px-1.5 cursor-pointer text-ink-muted leading-none hover:text-ink pointer-events-auto"; interface DatePickerProps { - value?: MaybeAccessor; + id?: string; + name?: string; + value?: string; + oninput?: JSX.InputEventHandlerUnion; onchange?: (value: string) => void; placeholder?: string; small?: boolean; diff --git a/go/jsruntime/uikit/Forms.tsx b/go/jsruntime/uikit/Forms.tsx index fc85fe69..52ac6d1a 100644 --- a/go/jsruntime/uikit/Forms.tsx +++ b/go/jsruntime/uikit/Forms.tsx @@ -107,8 +107,9 @@ export interface FormInputProps extends FormInputCommonProps, Pick, CommonInputAttrs | "type" | "max" | "min" | "step" | "accept"> { - small?: boolean; ref?: (el: HTMLInputElement) => void; + prefix?: JSXElement; + small?: boolean; class?: string; onDark?: boolean; } @@ -121,10 +122,6 @@ export interface FormTextareaProps onDark?: boolean; } -export interface FormInputGroupProps extends FormInputProps { - prefix?: JSXElement; -} - export interface FormNumberInputProps extends FormInputProps { int?: boolean; unsigned?: boolean; @@ -171,7 +168,7 @@ function _comboboxRootCls(className?: string, fieldWidth: ComboboxFieldWidth = " } export interface FormComboboxProps extends Pick, CommonSelectAttrs> { - options: FormSelectOption[]; + options?: FormSelectOption[]; placeholder?: string; searchable?: boolean; searchPlaceholder?: string; @@ -227,90 +224,104 @@ export interface FormMultiSelectTriggerProps { class?: string; } -export function FormInput(props: FormInputProps) { - const cls = () => _inputCls(props.small, props.error, props.success, props.class, props.onDark); - const inputValue = () => readAccessor(props.value, ""); - return
- typeof props.ref === "function" && props.ref(el)} - class={cls()} - type={props.type} - value={inputValue()} - placeholder={props.placeholder} - autocomplete={props.autocomplete || (props.passwordManagerIgnore ? "off" : undefined)} - inputmode={props.inputmode} - data-1p-ignore={props.passwordManagerIgnore} - data-lpignore={props.passwordManagerIgnore ? "other" : undefined} - data-form-type={props.passwordManagerIgnore} - data-bwignore={props.passwordManagerIgnore} - data-protonpass-ignore={props.passwordManagerIgnore} - disabled={props.disabled} - maxlength={props.maxlength} - min={props.min} - max={props.max} - step={props.step} - name={props.name} - id={props.id} - oninput={props.oninput} - onchange={props.onchange} - onBlur={props.onblur} - onFocus={props.onfocus} - onKeyDown={props.onkeydown} - - /> - - {props.error} - - - {props.success} - -
; +export function FormInputOld(props: FormInputProps) { + const [local, inputProps] = splitProps(props, [ + "prefix", + "small", + "error", + "success", + "class", + "onDark", + "ref", + "autocomplete", + "passwordManagerIgnore", + ]); + + return ( +
+ typeof local.ref === "function" && local.ref(el)} + class={_inputCls(local.small, local.error, local.success, local.class, local.onDark)} + autocomplete={local.autocomplete || (local.passwordManagerIgnore ? "off" : undefined)} + data-1p-ignore={local.passwordManagerIgnore} + data-lpignore={local.passwordManagerIgnore ? "other" : undefined} + data-form-type={local.passwordManagerIgnore} + data-bwignore={local.passwordManagerIgnore} + data-protonpass-ignore={local.passwordManagerIgnore} + {...inputProps} + /> + + {local.error} + + + {local.success} + +
+ ); } -export function FormInputGroup(props: FormInputGroupProps) { - const pfxCls = () => _prefixCls(props.small, props.error, props.onDark); - const inputCls = () => _inputCls(props.small, props.error, props.success, "rounded-l-none" + (props.class ? " " + props.class : ""), props.onDark); - const inputValue = () => readAccessor(props.value, ""); - return
-
- {props.prefix} - - typeof props.ref === "function" && props.ref(el)} - class={inputCls()} - type={props.type} - value={inputValue()} - placeholder={props.placeholder} - autocomplete={props.autocomplete || (props.passwordManagerIgnore ? "off" : undefined)} - inputmode={props.inputmode} - data-1p-ignore={props.passwordManagerIgnore} - data-lpignore={props.passwordManagerIgnore ? "other" : undefined} - data-form-type={props.passwordManagerIgnore} - data-bwignore={props.passwordManagerIgnore} - data-protonpass-ignore={props.passwordManagerIgnore} - disabled={props.disabled} - maxlength={props.maxlength} - name={props.name} - id={props.id} - oninput={props.oninput} - onchange={props.onchange} - onBlur={props.onblur} - onFocus={props.onfocus} - onKeyDown={props.onkeydown} - /> - -
- - {props.error} +export function FormInput(props: FormInputProps) { + const [local, inputProps] = splitProps(props, [ + "prefix", + "small", + "error", + "success", + "class", + "onDark", + "ref", + "autocomplete", + "passwordManagerIgnore", + ]); + + const cls = local.prefix ? ("rounded-l-none" + (local.class ? " " + local.class : "")) : local.class; + + const InputEl = () => ( + typeof local.ref === "function" && local.ref(el)} + class={_inputCls(local.small, local.error, local.success, cls, local.onDark)} + autocomplete={local.autocomplete || (local.passwordManagerIgnore ? "off" : undefined)} + data-1p-ignore={local.passwordManagerIgnore} + data-lpignore={local.passwordManagerIgnore ? "other" : undefined} + data-form-type={local.passwordManagerIgnore} + data-bwignore={local.passwordManagerIgnore} + data-protonpass-ignore={local.passwordManagerIgnore} + {...inputProps} + /> + ); + + return ( + + + + {local.error} + + + {local.success} + +
+ } + > +
+
+ {local.prefix} + +
+ + {local.error} + + + {local.success} + +
- - {props.success} - - ; + ); } export function FormNumberInput(props: FormNumberInputProps) { - const [local, inputProps] = splitProps(props, ["small", "ref", "class", "oninput", "int", "unsigned"]); + const [local, inputProps] = splitProps(props, ["oninput", "int", "unsigned"]); var regex: RegExp; if (local.int) { @@ -339,45 +350,11 @@ export function FormNumberInput(props: FormNumberInputProps) { } }; - return typeof local.ref === "function" && local.ref(el)} - type="text" - inputMode="decimal" - oninput={handleInput} - class={local.class} - small={local.small} - {...inputProps} - />; + return ; } -export function FormCarNumberInput(props: FormInputProps) { - const [local, inputProps] = splitProps(props, ["small", "ref", "class", "oninput"]); - - const handleInput = (e) => { - if (!e || !e.currentTarget) return; - const input = e.currentTarget; - const raw = input.value.toUpperCase().replace(/[^0-9A-Z]/g, ""); - const match = raw.match(/^([0-9]{0,2})([A-Z]{0,2})/); - input.value = match ? match[1] + match[2] : ""; - if (typeof local.oninput === "function") { - local.oninput(e); - } - }; - - return typeof local.ref === "function" && local.ref(el)} - type="text" - maxLength="4" - oninput={handleInput} - class={local.class} - small={local.small} - placeholder="e.g. 28" - {...inputProps} - />; -} - -export function FormCurrencyInput(props: {hideSymbol?: boolean} & FormInputProps) { - const [local, inputProps] = splitProps(props, ["small", "ref", "class", "oninput", "hideSymbol"]); +export function FormCurrencyInput(props: {showIcon?: boolean} & FormInputProps) { + const [local, inputProps] = splitProps(props, ["oninput", "showIcon"]); const handleInput = (e) => { if (!e || !e.currentTarget) return; @@ -396,32 +373,12 @@ export function FormCurrencyInput(props: {hideSymbol?: boolean} & FormInputProp } }; - if (local.hideSymbol) { - return typeof local.ref === "function" && local.ref(el)} - type="text" - inputMode="decimal" - oninput={handleInput} - class={local.class} - small={local.small} - {...inputProps} - />; - } else { - return typeof local.ref === "function" && local.ref(el)} - prefix="$" - type="text" - inputMode="decimal" - oninput={handleInput} - class={local.class} - small={local.small} - {...inputProps} - />; - } + return ; + } -export function FormPercentInput(props: FormInputProps) { - const [local, inputProps] = splitProps(props, ["small", "ref", "class", "oninput"]); +export function FormPercentInput(props: {showIcon?: boolean} & FormInputProps) { + const [local, inputProps] = splitProps(props, ["oninput", "showIcon"]); const handleInput = (e) => { if (!e || !e.currentTarget) return; @@ -436,20 +393,11 @@ export function FormPercentInput(props: FormInputProps) { } }; - return typeof local.ref === "function" && local.ref(el)} - prefix="%" - type="text" - inputMode="decimal" - oninput={handleInput} - class={local.class} - small={local.small} - {...inputProps} - />; + return ; } -export function FormPhoneInput(props: FormInputProps) { - const [local, inputProps] = splitProps(props, ["small", "ref", "class", "oninput"]); +export function FormPhoneInput(props: {showIcon?: boolean; icon?: string} & FormInputProps) { + const [local, inputProps] = splitProps(props, ["showIcon", "icon", "oninput"]); const handleInput = (e) => { if (!e || !e.currentTarget) return; @@ -473,138 +421,45 @@ export function FormPhoneInput(props: FormInputProps) { } }; - return typeof local.ref === "function" && local.ref(el)} - type="text" - inputMode="numeric" - oninput={handleInput} - class={local.class} - small={local.small} - placeholder="(555) 555-5555" - {...inputProps} - />; -} - -// @TODO replace this with showIcon prop -export function FormPhoneInputWithIcon(props: {icon?: string} & FormInputGroupProps) { - const [local, inputProps] = splitProps(props, ["icon", "small", "ref", "class", "oninput"]); - - const handleInput = (e) => { - if (!e || !e.currentTarget) return; - const input = e.currentTarget; - let digits = input.value.replace(/\D/g, ""); - digits = digits.slice(0, 10); - let formatted = ""; - if (digits.length > 0) { - formatted = "(" + digits.slice(0, 3); - } - if (digits.length > 3) { - formatted += ") " + digits.slice(3, 6); - } - if (digits.length > 6) { - formatted += "-" + digits.slice(6, 10); - } - input.value = formatted; - if (typeof local.oninput === "function") { - local.oninput(e); - } - }; - - const iconEl = ; - - return typeof local.ref === "function" && local.ref(el)} - type="text" - inputMode="numeric" - oninput={handleInput} - class={local.class} - small={local.small} - placeholder="(555) 555-5555" - {...inputProps} - />; + return ( + : null} + inputMode="numeric" + oninput={handleInput} + placeholder="(555) 555-5555" + {...inputProps} + /> + ); } export function FormEmailInput(props: {showIcon?: boolean} & FormInputProps) { - const [local, inputProps] = splitProps(props, ["showIcon", "small", "ref", "class", "error", "success", "onDark"]); - const showIcon = () => local.showIcon !== false; - const pfxCls = () => _prefixCls(local.small, local.error, local.onDark); - const inputCls = () => _inputCls(local.small, local.error, local.success, "rounded-l-none" + (local.class ? " " + local.class : ""), local.onDark); + const [local, inputProps] = splitProps(props, ["showIcon"]); - return typeof local.ref === "function" && local.ref(el)} + prefix={local.showIcon !== false ? : null} type="email" inputMode="email" - class={local.class} - small={local.small} - error={local.error} - success={local.success} - onDark={local.onDark} {...inputProps} - />}> -
-
- - - - - typeof local.ref === "function" && local.ref(el)} - type="email" - inputMode="email" - class={inputCls()} - {...inputProps} - /> - -
- - {local.error} - - - {local.success} - -
-
; + /> + ); } -export function FormURLInput(props: {showIcon: boolean} & FormInputProps) { - const [local, inputProps] = splitProps(props, ["showIcon", "small", "ref", "class", "onDark"]); - const showIcon = () => local.showIcon !== false; - const pfxCls = () => _prefixCls(local.small, false, local.onDark); - const inputCls = () => _inputCls(local.small, false, false, "rounded-l-none" + (local.class ? " " + local.class : ""), local.onDark); +export function FormURLInput(props: {showIcon?: boolean} & FormInputProps) { + const [local, inputProps] = splitProps(props, ["showIcon"]); - return typeof local.ref === "function" && local.ref(el)} + prefix={local.showIcon !== false ? : null} type="text" inputMode="url" - class={local.class} - small={local.small} - onDark={local.onDark} {...inputProps} - />}> -
-
- - - - - typeof local.ref === "function" && local.ref(el)} - type="text" - inputMode="url" - class={inputCls()} - {...inputProps} - /> - -
-
-
; + /> + ); } export function FormZipCodeInput(props: FormInputProps) { - const [local, inputProps] = splitProps(props, ["small", "ref", "class", "oninput"]); + const [local, inputProps] = splitProps(props, ["oninput"]); const handleInput = (e) => { if (!e || !e.currentTarget) return; @@ -617,18 +472,15 @@ export function FormZipCodeInput(props: FormInputProps) { return b ? a + "-" + b : a; }); - if (typeof props.oninput === "function") { - props.oninput(e); + if (typeof local.oninput === "function") { + local.oninput(e); } }; return typeof local.ref === "function" && local.ref(el)} type="text" inputMode="numeric" oninput={handleInput} - class={local.class} - small={local.small} placeholder="12345" {...inputProps} />; @@ -807,7 +659,6 @@ export function FormSignaturePad(props: { value?: () => string; onchange?: (svg: // @TODO Fix this export function FormSelect(props: FormSelectProps & { children?: JSXElement }) { const [local, selectProps] = splitProps(props, ["small", "ref", "class", "children", "value", "onDark"]); - const cls = () => _inputCls(local.small, false, false, local.class, local.onDark); let selectEl; // Set value via deferred effect so it runs AFTER children (