UI/UX-Prototyp der Analytics-Dashboards in HiveGrid, fachlich auf einen SHK-/Handwerksbetrieb zugeschnitten. Ziel ist die Übergabe an die Entwicklung, nicht der Produktivbetrieb. Inhalt: sechs Seiten (Übersicht, Finanzen, Aufträge, Kunden, Team, Komponenten), 26 KPIs, neun Chart-Bausteine, Filtersystem mit seitenabhängiger Relevanz und ein deterministischer Demo-Datensatz (420 Kunden, 1.900 Aufträge, Stichtag 30.07.2026). Drei Quellen sind zusammengeführt: die fachliche Planung mit dem Branchenpartner (Januar 2026), die Subframe-Komponentenbibliothek aus dem HiveGrid-Pitch und IBM Carbon Data Visualization für die Diagrammregeln. Herkunft und bewusste Abweichungen stehen in docs/HERKUNFT.md. Dokumentation: README, HANDOVER (was echte Logik braucht), docs/DESIGN-SYSTEM, KPI-KATALOG, DATENMODELL, NAVIGATION, HERKUNFT, ZIELSYSTEM (Einbauort in HiveGrid Cloud) und LEITPLANKEN (welche Cloud-Screens als Vorbild taugen). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPeZNwSeU7qG3RRCwaxEZX
108 lines
4.1 KiB
TypeScript
108 lines
4.1 KiB
TypeScript
"use client";
|
|
/*
|
|
* Documentation:
|
|
* Link Button — https://app.subframe.com/f023064a5ca0/library?component=Link+Button_a4ee726a-774c-4091-8c49-55b659356024
|
|
*/
|
|
|
|
import React from "react";
|
|
import { FeatherArrowRight } from "@subframe/core";
|
|
import * as SubframeCore from "@subframe/core";
|
|
import * as SubframeUtils from "../../utils";
|
|
|
|
export interface LinkButtonRootProps
|
|
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
disabled?: boolean;
|
|
variant?: "primary" | "subtle";
|
|
size?: "large" | "medium" | "small";
|
|
icon?: React.ReactNode;
|
|
children?: React.ReactNode;
|
|
iconRight?: React.ReactNode;
|
|
hideIcon?: boolean;
|
|
underline?: boolean;
|
|
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
|
className?: string;
|
|
}
|
|
|
|
const LinkButtonRoot = React.forwardRef<HTMLButtonElement, LinkButtonRootProps>(
|
|
function LinkButtonRoot(
|
|
{
|
|
disabled = false,
|
|
variant = "primary",
|
|
size = "medium",
|
|
icon = null,
|
|
children,
|
|
iconRight = <FeatherArrowRight />,
|
|
hideIcon = false,
|
|
underline = false,
|
|
className,
|
|
type = "button",
|
|
...otherProps
|
|
}: LinkButtonRootProps,
|
|
ref
|
|
) {
|
|
return (
|
|
<button
|
|
className={SubframeUtils.twClassNames(
|
|
"group/a4ee726a flex cursor-pointer items-start gap-2 border-none bg-transparent text-left",
|
|
className
|
|
)}
|
|
ref={ref}
|
|
type={type}
|
|
disabled={disabled}
|
|
{...otherProps}
|
|
>
|
|
{icon ? (
|
|
<SubframeCore.IconWrapper
|
|
className={SubframeUtils.twClassNames(
|
|
"text-body-default-regular-150 font-body-default-regular-150 text-icon-feedback-link-default group-hover/a4ee726a:text-icon-feedback-link-hover group-active/a4ee726a:text-icon-feedback-link-active group-disabled/a4ee726a:text-icon-neutral-disabled",
|
|
{
|
|
hidden: hideIcon,
|
|
"text-body-small-regular-150 font-body-small-regular-150":
|
|
size === "small",
|
|
"text-body-large font-body-large": size === "large",
|
|
"text-icon-neutral-primary": variant === "subtle",
|
|
}
|
|
)}
|
|
>
|
|
{icon}
|
|
</SubframeCore.IconWrapper>
|
|
) : null}
|
|
{children ? (
|
|
<span
|
|
className={SubframeUtils.twClassNames(
|
|
"min-w-[0px] break-words text-body-default-regular-0 font-body-default-regular-0 text-text-feedback-link-default group-hover/a4ee726a:text-text-feedback-link-hover group-active/a4ee726a:text-text-feedback-link-active group-disabled/a4ee726a:text-text-neutral-disabled group-hover/a4ee726a:group-disabled/a4ee726a:text-text-neutral-disabled group-active/a4ee726a:group-disabled/a4ee726a:text-text-neutral-disabled",
|
|
{
|
|
underline: underline,
|
|
"text-body-small-regular-0 font-body-small-regular-0":
|
|
size === "small",
|
|
"text-body-large-0 font-body-large-0": size === "large",
|
|
"text-text-neutral-primary": variant === "subtle",
|
|
}
|
|
)}
|
|
>
|
|
{children}
|
|
</span>
|
|
) : null}
|
|
{iconRight ? (
|
|
<SubframeCore.IconWrapper
|
|
className={SubframeUtils.twClassNames(
|
|
"text-body-default-regular-0 font-body-default-regular-0 text-icon-feedback-link-default group-hover/a4ee726a:text-icon-feedback-link-hover group-active/a4ee726a:text-icon-feedback-link-active group-disabled/a4ee726a:text-icon-neutral-disabled group-hover/a4ee726a:group-disabled/a4ee726a:text-icon-neutral-disabled group-active/a4ee726a:group-disabled/a4ee726a:text-text-neutral-disabled",
|
|
{
|
|
hidden: hideIcon,
|
|
"text-body-small-regular-150 font-body-small-regular-150":
|
|
size === "small",
|
|
"text-body-large font-body-large": size === "large",
|
|
"text-icon-neutral-primary": variant === "subtle",
|
|
}
|
|
)}
|
|
>
|
|
{iconRight}
|
|
</SubframeCore.IconWrapper>
|
|
) : null}
|
|
</button>
|
|
);
|
|
}
|
|
);
|
|
|
|
export const LinkButton = LinkButtonRoot;
|