feat(ui): wire platform core primitives
This commit is contained in:
parent
5009a22675
commit
b73c969d14
@ -12,6 +12,8 @@
|
||||
"smoke:compose": "bash scripts/compose-smoke.sh",
|
||||
"seed:bootstrap": "pnpm --filter @notelett/backend run bootstrap:seed",
|
||||
"audit:release-guards": "bash scripts/release-guard-audit.sh",
|
||||
"audit:ui": "bash scripts/ui-drift-audit.sh",
|
||||
"audit:ui:strict": "bash scripts/ui-drift-audit.sh --strict",
|
||||
"dependency:health": "bash scripts/dependency-health.sh",
|
||||
"verify": "pnpm run typecheck && pnpm run test && pnpm run build",
|
||||
"prepare": "husky"
|
||||
|
||||
42
scripts/ui-drift-audit.sh
Normal file
42
scripts/ui-drift-audit.sh
Normal file
@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
STRICT=0
|
||||
if [[ "${1:-}" == "--strict" ]]; then
|
||||
STRICT=1
|
||||
fi
|
||||
|
||||
ROOT="$(git rev-parse --show-toplevel)"
|
||||
cd "$ROOT"
|
||||
|
||||
report() {
|
||||
local title="$1"
|
||||
local pattern="$2"
|
||||
shift 2
|
||||
local matches
|
||||
matches="$(rg -n "$pattern" "$@" --glob '!**/*.test.*' || true)"
|
||||
|
||||
echo "=== $title ==="
|
||||
if [[ -z "$matches" ]]; then
|
||||
echo "ok: no matches"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "$matches"
|
||||
echo
|
||||
return 1
|
||||
}
|
||||
|
||||
failures=0
|
||||
|
||||
report "Raw interactive controls" '<button|<input|<textarea|<select' web/src/app web/src/components || failures=$((failures + 1))
|
||||
report "Legacy global surface classes" 'className="[^"]*(badge|surface-card|surface-muted|input-shell)' web/src/app web/src/components || failures=$((failures + 1))
|
||||
report "Hardcoded color literals" '#[0-9a-fA-F]{3,8}|rgba?\(' web/src/app web/src/components || failures=$((failures + 1))
|
||||
report "Direct @bytelyst/ui imports outside adapter" 'from "@bytelyst/ui"|from '\''@bytelyst/ui'\''' web/src/app web/src/components --glob '!web/src/components/ui/Primitives.tsx' || failures=$((failures + 1))
|
||||
|
||||
if [[ "$STRICT" == "1" && "$failures" -gt 0 ]]; then
|
||||
echo "UI drift audit failed in strict mode with $failures category/categories containing matches." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "UI drift audit completed with $failures non-empty category/categories."
|
||||
@ -2,9 +2,37 @@ import {
|
||||
Badge as BytelystBadge,
|
||||
Button as BytelystButton,
|
||||
Card as BytelystCard,
|
||||
DiffCard as BytelystDiffCard,
|
||||
IconButton as BytelystIconButton,
|
||||
Input as BytelystInput,
|
||||
Label as BytelystLabel,
|
||||
ListItemButton as BytelystListItemButton,
|
||||
Panel as BytelystPanel,
|
||||
PanelBody as BytelystPanelBody,
|
||||
PanelDescription as BytelystPanelDescription,
|
||||
PanelHeader as BytelystPanelHeader,
|
||||
PanelTitle as BytelystPanelTitle,
|
||||
Select as BytelystSelect,
|
||||
StatusBadge as BytelystStatusBadge,
|
||||
Textarea as BytelystTextarea,
|
||||
Timeline as BytelystTimeline,
|
||||
type BadgeProps,
|
||||
type ButtonProps,
|
||||
type CardProps,
|
||||
type DiffCardProps,
|
||||
type IconButtonProps,
|
||||
type InputProps,
|
||||
type LabelProps,
|
||||
type ListItemButtonProps,
|
||||
type PanelBodyProps,
|
||||
type PanelDescriptionProps,
|
||||
type PanelHeaderProps,
|
||||
type PanelProps,
|
||||
type PanelTitleProps,
|
||||
type SelectProps,
|
||||
type StatusBadgeProps,
|
||||
type TextareaProps,
|
||||
type TimelineProps,
|
||||
} from "@bytelyst/ui";
|
||||
|
||||
function mergeClassNames(...classes: Array<string | undefined>) {
|
||||
@ -46,3 +74,112 @@ export function Card({ className, ...props }: CardProps) {
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function Panel({ className, ...props }: PanelProps) {
|
||||
return (
|
||||
<BytelystPanel
|
||||
className={mergeClassNames(
|
||||
"rounded-[var(--nl-radius-md)] border-[var(--nl-border-default)] bg-[var(--nl-surface-card-translucent)] shadow-[var(--nl-elevation-md)]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function PanelHeader({ className, ...props }: PanelHeaderProps) {
|
||||
return <BytelystPanelHeader className={mergeClassNames("gap-[var(--nl-space-3)]", className)} {...props} />;
|
||||
}
|
||||
|
||||
export function PanelBody({ className, ...props }: PanelBodyProps) {
|
||||
return <BytelystPanelBody className={mergeClassNames("gap-[var(--nl-space-3)]", className)} {...props} />;
|
||||
}
|
||||
|
||||
export function PanelTitle({ className, ...props }: PanelTitleProps) {
|
||||
return <BytelystPanelTitle className={mergeClassNames("text-[var(--nl-text-primary)]", className)} {...props} />;
|
||||
}
|
||||
|
||||
export function PanelDescription({ className, ...props }: PanelDescriptionProps) {
|
||||
return <BytelystPanelDescription className={mergeClassNames("text-[var(--nl-text-secondary)]", className)} {...props} />;
|
||||
}
|
||||
|
||||
export function IconButton({ className, ...props }: IconButtonProps) {
|
||||
return (
|
||||
<BytelystIconButton
|
||||
className={mergeClassNames(
|
||||
"rounded-[var(--nl-radius-sm)] focus-visible:ring-[var(--nl-accent-primary)]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function ListItemButton({ className, ...props }: ListItemButtonProps) {
|
||||
return (
|
||||
<BytelystListItemButton
|
||||
className={mergeClassNames(
|
||||
"rounded-[var(--nl-radius-sm)] border-[var(--nl-border-default)] bg-[var(--nl-surface-muted-translucent)] text-[var(--nl-text-primary)]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function StatusBadge({ className, ...props }: StatusBadgeProps) {
|
||||
return (
|
||||
<BytelystStatusBadge
|
||||
className={mergeClassNames("border-[var(--nl-border-default)] text-[var(--nl-text-primary)]", className)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function Input({ className, ...props }: InputProps) {
|
||||
return (
|
||||
<BytelystInput
|
||||
className={mergeClassNames(
|
||||
"rounded-[var(--nl-radius-sm)] border-[var(--nl-border-default)] bg-[var(--nl-input-bg)] text-[var(--nl-text-primary)] focus:ring-[var(--nl-accent-primary)]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function Textarea({ className, ...props }: TextareaProps) {
|
||||
return (
|
||||
<BytelystTextarea
|
||||
className={mergeClassNames(
|
||||
"rounded-[var(--nl-radius-sm)] border-[var(--nl-border-default)] bg-[var(--nl-input-bg)] text-[var(--nl-text-primary)] focus:ring-[var(--nl-accent-primary)]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function Select({ className, ...props }: SelectProps) {
|
||||
return (
|
||||
<BytelystSelect
|
||||
className={mergeClassNames(
|
||||
"rounded-[var(--nl-radius-sm)] border-[var(--nl-border-default)] bg-[var(--nl-input-bg)] text-[var(--nl-text-primary)] focus:ring-[var(--nl-accent-primary)]",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function Label({ className, ...props }: LabelProps) {
|
||||
return <BytelystLabel className={mergeClassNames("text-[var(--nl-text-secondary)]", className)} {...props} />;
|
||||
}
|
||||
|
||||
export function Timeline({ className, ...props }: TimelineProps) {
|
||||
return <BytelystTimeline className={mergeClassNames("gap-[var(--nl-space-3)]", className)} {...props} />;
|
||||
}
|
||||
|
||||
export function DiffCard({ className, ...props }: DiffCardProps) {
|
||||
return <BytelystDiffCard className={mergeClassNames("gap-[var(--nl-space-3)]", className)} {...props} />;
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user