Package library as @moduletrace/twui npm package

- Move components/ to src/ and compile to dist/ with tsc
- Add root barrel export plus deep-import subpath exports and base.css export
- Post-compile step: copy base.css, add .js import extensions, mark client modules
- Add publish.sh and .npmrc for the Gitea npm registry
- Fix type errors, guard TinyMCE env access, drop ace-builds type dependency
- Rename clashing props type names, remove Next.js scaffolding and unused deps

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-18 10:17:54 +01:00
co-authored by Claude Opus 5.5
parent d19d412066
commit 11da9b89e7
148 changed files with 937 additions and 500 deletions
+79
View File
@@ -0,0 +1,79 @@
import type {
ComponentProps,
DetailedHTMLProps,
InputHTMLAttributes,
LabelHTMLAttributes,
} from "react";
import Row from "../layout/Row";
import twuiSlugify from "../utils/slugify";
import twuiSlugToNormalText from "../utils/slug-to-normal-text";
import { twMerge } from "tailwind-merge";
type Value = {
value: string;
title?: string;
default?: boolean;
};
export type TWUI_FORM_RADIO_PROPS = {
values: Value[];
name: string;
inputProps?: DetailedHTMLProps<
InputHTMLAttributes<HTMLInputElement>,
HTMLInputElement
>;
labelProps?: DetailedHTMLProps<
LabelHTMLAttributes<HTMLLabelElement>,
HTMLLabelElement
>;
wrapperProps?: ComponentProps<typeof Row>;
changeHandler?: (value: string) => void;
};
/**
* # Form Radios Component
* @className twui-textarea
*/
export default function Radios({
values,
name,
inputProps,
labelProps,
wrapperProps,
changeHandler,
}: TWUI_FORM_RADIO_PROPS) {
const finalName = twuiSlugify(name);
const finalTitle = twuiSlugToNormalText(finalName);
return (
<Row
title={finalTitle}
{...wrapperProps}
className={twMerge("gap-4", wrapperProps?.className)}
>
{values.map((v, i) => {
const valueName = twuiSlugify(`${finalName}-${v.value}`);
const valueTitle = v.title || twuiSlugToNormalText(v.value);
return (
<Row key={i} className="gap-1.5">
<input
id={valueName}
type="radio"
defaultChecked={v.default}
name={finalName}
onChange={(e) => {
const targetValue = v.value;
changeHandler?.(targetValue);
}}
{...inputProps}
/>
<label htmlFor={valueName} {...labelProps}>
{valueTitle}
</label>
</Row>
);
})}
</Row>
);
}