feat(dashboard): Analytics-Dashboard als Gestaltungsvorlage zur Übergabe
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
This commit is contained in:
commit
b8bf987d0e
250 files changed
+33153
No files matched your search
@@ -0,0 +1,157 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Accordion — https://app.subframe.com/f023064a5ca0/library?component=Accordion_d2e81e20-863a-4027-826a-991d8910efd9
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherChevronRight } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface ChevronProps
|
||||
extends React.ComponentProps<typeof SubframeCore.Collapsible.Chevron> {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Chevron = React.forwardRef<
|
||||
React.ElementRef<typeof FeatherChevronRight>,
|
||||
ChevronProps
|
||||
>(function Chevron({ className, ...otherProps }: ChevronProps, ref) {
|
||||
return (
|
||||
<SubframeCore.Collapsible.Chevron {...otherProps}>
|
||||
<FeatherChevronRight
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-default-regular-150 font-body-default-regular-150 text-icon-neutral-primary group-data-[state=open]/d2e81e20:-rotate-90 group-aria-[invalid=true]/d2e81e20:text-icon-feedback-negative group-aria-[disabled=true]/d2e81e20:text-icon-neutral-disabled",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
/>
|
||||
</SubframeCore.Collapsible.Chevron>
|
||||
);
|
||||
});
|
||||
|
||||
export interface ContentProps
|
||||
extends React.ComponentProps<typeof SubframeCore.Collapsible.Content> {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Content = React.forwardRef<HTMLDivElement, ContentProps>(function Content(
|
||||
{ children, className, ...otherProps }: ContentProps,
|
||||
ref
|
||||
) {
|
||||
return children ? (
|
||||
<SubframeCore.Collapsible.Content asChild={true} {...otherProps}>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full flex-col items-start gap-2",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</SubframeCore.Collapsible.Content>
|
||||
) : null;
|
||||
});
|
||||
|
||||
export interface TriggerProps
|
||||
extends React.ComponentProps<typeof SubframeCore.Collapsible.Trigger> {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Trigger = React.forwardRef<HTMLDivElement, TriggerProps>(function Trigger(
|
||||
{ children, className, ...otherProps }: TriggerProps,
|
||||
ref
|
||||
) {
|
||||
return children ? (
|
||||
<SubframeCore.Collapsible.Trigger asChild={true} {...otherProps}>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full cursor-pointer flex-col items-start gap-2",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</SubframeCore.Collapsible.Trigger>
|
||||
) : null;
|
||||
});
|
||||
|
||||
export interface AccordionRootProps
|
||||
extends React.ComponentProps<typeof SubframeCore.Collapsible.Root> {
|
||||
disabled?: boolean;
|
||||
error?: boolean;
|
||||
open?: boolean;
|
||||
variant?: "card" | "ghost";
|
||||
trigger?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const AccordionRoot = React.forwardRef<HTMLDivElement, AccordionRootProps>(
|
||||
function AccordionRoot(
|
||||
{
|
||||
disabled = false,
|
||||
error = false,
|
||||
open,
|
||||
variant = "card",
|
||||
trigger,
|
||||
children,
|
||||
className,
|
||||
defaultOpen = false,
|
||||
...otherProps
|
||||
}: AccordionRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<SubframeCore.Collapsible.Root
|
||||
open={open}
|
||||
defaultOpen={defaultOpen}
|
||||
asChild={true}
|
||||
disabled={disabled}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/d2e81e20 flex w-full cursor-pointer flex-col items-start rounded-md border border-solid border-border-default bg-surface-neutral-secondary hover:bg-surface-neutral-tertiary",
|
||||
{
|
||||
"border-none bg-transparent hover:bg-transparent":
|
||||
variant === "ghost",
|
||||
"border-2 border-solid border-border-negative-solid hover:border-border-negative-solid":
|
||||
error,
|
||||
"border border-solid border-border-disabled bg-surface-neutral-disabled opacity-40 pointer-events-none hover:bg-surface-neutral-disabled hover:border-border-disabled":
|
||||
disabled,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
<Trigger>
|
||||
{trigger ? (
|
||||
<div className="flex w-full grow shrink-0 basis-0 flex-col items-start group-data-[state=open]/d2e81e20:flex-none">
|
||||
{trigger}
|
||||
</div>
|
||||
) : null}
|
||||
</Trigger>
|
||||
<Content>
|
||||
{children ? (
|
||||
<div className="flex w-full grow shrink-0 basis-0 flex-col items-start">
|
||||
{children}
|
||||
</div>
|
||||
) : null}
|
||||
</Content>
|
||||
</div>
|
||||
</SubframeCore.Collapsible.Root>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const Accordion = Object.assign(AccordionRoot, {
|
||||
Chevron,
|
||||
Content,
|
||||
Trigger,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { Accordion as AccordionComponent } from "./Accordion";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const Accordion = AccordionComponent;
|
||||
@@ -0,0 +1,158 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Accordion Nested — https://app.subframe.com/f023064a5ca0/library?component=Accordion+Nested_91a645f4-5a9c-4c61-be00-d85b9d554e91
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherChevronRight } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface ChevronProps
|
||||
extends React.ComponentProps<typeof SubframeCore.Collapsible.Chevron> {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Chevron = React.forwardRef<
|
||||
React.ElementRef<typeof FeatherChevronRight>,
|
||||
ChevronProps
|
||||
>(function Chevron({ className, ...otherProps }: ChevronProps, ref) {
|
||||
return (
|
||||
<SubframeCore.Collapsible.Chevron {...otherProps}>
|
||||
<FeatherChevronRight
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-default-regular-150 font-body-default-regular-150 text-icon-neutral-primary transition-transform group-data-[state=open]/91a645f4:-rotate-90 group-aria-[invalid=true]/91a645f4:text-icon-feedback-negative group-aria-[disabled=true]/91a645f4:text-icon-neutral-disabled",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
/>
|
||||
</SubframeCore.Collapsible.Chevron>
|
||||
);
|
||||
});
|
||||
|
||||
export interface ContentProps
|
||||
extends React.ComponentProps<typeof SubframeCore.Collapsible.Content> {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Content = React.forwardRef<HTMLDivElement, ContentProps>(function Content(
|
||||
{ children, className, ...otherProps }: ContentProps,
|
||||
ref
|
||||
) {
|
||||
return children ? (
|
||||
<SubframeCore.Collapsible.Content asChild={true} {...otherProps}>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full flex-col items-start gap-2",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</SubframeCore.Collapsible.Content>
|
||||
) : null;
|
||||
});
|
||||
|
||||
export interface TriggerProps
|
||||
extends React.ComponentProps<typeof SubframeCore.Collapsible.Trigger> {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Trigger = React.forwardRef<HTMLDivElement, TriggerProps>(function Trigger(
|
||||
{ children, className, ...otherProps }: TriggerProps,
|
||||
ref
|
||||
) {
|
||||
return children ? (
|
||||
<SubframeCore.Collapsible.Trigger asChild={true} {...otherProps}>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full cursor-pointer flex-col items-start gap-2",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</SubframeCore.Collapsible.Trigger>
|
||||
) : null;
|
||||
});
|
||||
|
||||
export interface AccordionNestedRootProps
|
||||
extends React.ComponentProps<typeof SubframeCore.Collapsible.Root> {
|
||||
disabled?: boolean;
|
||||
error?: boolean;
|
||||
open?: boolean;
|
||||
variant?: "card" | "ghost";
|
||||
trigger?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const AccordionNestedRoot = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
AccordionNestedRootProps
|
||||
>(function AccordionNestedRoot(
|
||||
{
|
||||
disabled = false,
|
||||
error = false,
|
||||
open,
|
||||
variant = "card",
|
||||
trigger,
|
||||
children,
|
||||
className,
|
||||
defaultOpen = false,
|
||||
...otherProps
|
||||
}: AccordionNestedRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<SubframeCore.Collapsible.Root
|
||||
open={open}
|
||||
defaultOpen={defaultOpen}
|
||||
asChild={true}
|
||||
disabled={disabled}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/91a645f4 flex w-full cursor-pointer flex-col items-start rounded-md border border-solid border-border-default bg-surface-neutral-secondary hover:bg-surface-neutral-tertiary",
|
||||
{
|
||||
"border-none bg-transparent hover:bg-transparent":
|
||||
variant === "ghost",
|
||||
"border-2 border-solid border-border-negative-solid hover:border-border-negative-solid":
|
||||
error,
|
||||
"border border-solid border-border-disabled bg-surface-neutral-disabled opacity-40 pointer-events-none hover:bg-surface-neutral-disabled hover:border-border-disabled":
|
||||
disabled,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
<Trigger>
|
||||
{trigger ? (
|
||||
<div className="flex w-full grow shrink-0 basis-0 flex-col items-start group-data-[state=open]/91a645f4:flex-none">
|
||||
{trigger}
|
||||
</div>
|
||||
) : null}
|
||||
</Trigger>
|
||||
<Content>
|
||||
{children ? (
|
||||
<div className="flex w-full grow shrink-0 basis-0 flex-col items-start">
|
||||
{children}
|
||||
</div>
|
||||
) : null}
|
||||
</Content>
|
||||
</div>
|
||||
</SubframeCore.Collapsible.Root>
|
||||
);
|
||||
});
|
||||
|
||||
export const AccordionNested = Object.assign(AccordionNestedRoot, {
|
||||
Chevron,
|
||||
Content,
|
||||
Trigger,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { AccordionNested as AccordionNestedComponent } from "./AccordionNested";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const AccordionNested = AccordionNestedComponent;
|
||||
@@ -0,0 +1,28 @@
|
||||
## Wann verwenden
|
||||
|
||||
Überall, wo eine postalische Adresse erfasst oder bearbeitet wird (Kontakt, Projekt, Lieferadresse, Baustelle, Rechnungsadresse). Immer diese Komponente statt eines eigenen Adressformulars — nur so bleibt Feldreihenfolge und Beschriftung im ganzen Produkt identisch.
|
||||
|
||||
## Feldreihenfolge ist nicht verhandelbar
|
||||
|
||||
Die Reihenfolge folgt DIN 5008 / DHL-Adresslogik, also der Reihenfolge, in der eine Adresse auf ein Label gedruckt wird: Firma → Name → Straße + Nr. → Adresszusatz → PLZ + Ort → Land. Nutzer tippen Adressen als zusammenhängenden Block ab; jede Umsortierung erzeugt Lesefehler und Zahlendreher. **Land steht immer zuletzt**, nie oben.
|
||||
|
||||
PLZ steht links vor dem Ort (DE/AT/CH-Konvention), Hausnummer rechts nach der Straße. Beide Paare bleiben in einer Zeile, damit sie als eine Information gelesen werden.
|
||||
|
||||
## Pflicht vs. optional
|
||||
|
||||
- Fachlich nötig für Versand und Routing: Name (oder Firma), Straße, Nr., PLZ, Ort, Land.
|
||||
- Optional und im Platzhalter als „(optional)" markiert: Firma, Adresszusatz. Adresszusatz nie für Straßendaten missbrauchen — er ist für Etage, Gebäude, c/o.
|
||||
|
||||
## Notiz zur Adresse
|
||||
|
||||
Interne Zustellinformation, kein Adressbestandteil: Zufahrt, Schlüsselübergabe, Parksituation, Torcode. Deshalb sitzt sie **unter der Trennlinie**, außerhalb des Adressblocks, und der Hilfetext sagt explizit, dass sie nicht auf Versandlabels gedruckt wird. Diese Trennung nicht auflösen und die Notiz nicht nach oben ziehen.
|
||||
|
||||
## Props im Einsatz
|
||||
|
||||
- `hideAddressType`: einschalten, wenn der Kontext den Typ schon festlegt (z. B. Dialog heißt „Rechnungsadresse ändern"). Doppelte Angabe derselben Information vermeiden.
|
||||
- `hideNote`: nur einschalten, wo keine Zustellung stattfindet (z. B. reine Rechnungsadresse).
|
||||
- `title` / `subtitle`: Titel benennt die Aktion („Adresse hinzufügen", „Lieferadresse bearbeiten"), der Untertitel sagt, wofür die Daten verwendet werden — nicht mit Bedienhinweisen füllen.
|
||||
|
||||
## Beschriftung
|
||||
|
||||
Labels bleiben kurz und in Nutzersprache („Nr.", nicht „Hausnummer/Zusatz"), Platzhalter zeigen ein echtes Beispiel („10115", „12a") statt einer Formatanweisung. Keine Sternchen für Pflichtfelder — optionale Felder werden markiert, nicht umgekehrt.
|
||||
@@ -0,0 +1,478 @@
|
||||
// @subframe/sync-disable
|
||||
// @ts-nocheck
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Address Dialog — https://app.subframe.com/f023064a5ca0/library?component=Address+Dialog_b3a6c4d9-a908-4acc-881c-132a867b5ef6
|
||||
* Avatar — https://app.subframe.com/f023064a5ca0/library?component=Avatar_bec25ae6-5010-4485-b46b-cf79e3943ab2
|
||||
* Button — https://app.subframe.com/f023064a5ca0/library?component=Button_3b777358-b86b-40af-9327-891efc6826fe
|
||||
* Checkbox — https://app.subframe.com/f023064a5ca0/library?component=Checkbox_3816e3b5-c48c-499b-b45e-0777c6972523
|
||||
* Dialog — https://app.subframe.com/f023064a5ca0/library?component=Dialog_ca59db17-43fb-4247-8094-3c55162e902d
|
||||
* Icon Button — https://app.subframe.com/f023064a5ca0/library?component=Icon+Button_af9405b1-8c54-4e01-9786-5aad308224f6
|
||||
* Icon with Background — https://app.subframe.com/f023064a5ca0/library?component=Icon+with+Background_4a18e9fc-cea3-46ca-8c53-2412462342ad
|
||||
* Search Input — https://app.subframe.com/f023064a5ca0/library?component=Search+Input_75a35ed3-52c0-4435-8586-e390eb77d723
|
||||
* Select — https://app.subframe.com/f023064a5ca0/library?component=Select_bb88f90b-8c43-4b73-9c2f-3558ce7838f3
|
||||
* Text Area — https://app.subframe.com/f023064a5ca0/library?component=Text+Area_4ec05ee8-8f1c-46b2-b863-5419aa7f5cce
|
||||
* Text Input — https://app.subframe.com/f023064a5ca0/library?component=Text+Input_be48ca43-f8e7-4c0e-8870-d219ea11abfe
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherArrowRight } from "@subframe/core";
|
||||
import { FeatherCheck } from "@subframe/core";
|
||||
import { FeatherChevronDown } from "@subframe/core";
|
||||
import { FeatherMapPin } from "@subframe/core";
|
||||
import { FeatherUserSearch } from "@subframe/core";
|
||||
import { FeatherX } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
import { Avatar } from "../Avatar";
|
||||
import { Button } from "../Button";
|
||||
import { Checkbox } from "../Checkbox";
|
||||
import { Dialog } from "../Dialog";
|
||||
import { IconButton } from "../IconButton";
|
||||
import { IconWithBackground } from "../IconWithBackground";
|
||||
import { SearchInput } from "../SearchInput";
|
||||
import { Select } from "../Select";
|
||||
import { TextArea } from "../TextArea";
|
||||
import { TextInput } from "../TextInput";
|
||||
|
||||
export interface AddressDialogRootProps
|
||||
extends React.ComponentProps<typeof Dialog> {
|
||||
hideAddressType?: boolean;
|
||||
hideNote?: boolean;
|
||||
hideCompany?: boolean;
|
||||
hideDefaultAddress?: boolean;
|
||||
hideImport?: boolean;
|
||||
title?: React.ReactNode;
|
||||
subtitle?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const AddressDialogRoot = React.forwardRef<
|
||||
React.ElementRef<typeof Dialog>,
|
||||
AddressDialogRootProps
|
||||
>(function AddressDialogRoot(
|
||||
{
|
||||
hideAddressType = false,
|
||||
hideNote = false,
|
||||
hideCompany = false,
|
||||
hideDefaultAddress = false,
|
||||
hideImport = false,
|
||||
title,
|
||||
subtitle,
|
||||
className,
|
||||
...otherProps
|
||||
}: AddressDialogRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<Dialog
|
||||
className={SubframeUtils.twClassNames("group/b3a6c4d9", className)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<Dialog.Content
|
||||
className="mobile:w-full mobile:max-w-[calc(100vw-32px)]"
|
||||
size="medium"
|
||||
hideCloseIcon={true}
|
||||
>
|
||||
<div className="flex w-full flex-col items-start gap-4">
|
||||
<div className="flex w-full items-start justify-between gap-3">
|
||||
<div className="flex items-start gap-3">
|
||||
<IconWithBackground
|
||||
variant="primary"
|
||||
size="medium"
|
||||
icon={<FeatherMapPin />}
|
||||
/>
|
||||
<div className="flex flex-col items-start gap-1">
|
||||
{title ? (
|
||||
<span className="text-heading-0 font-heading-0 text-text-neutral-primary">
|
||||
{title}
|
||||
</span>
|
||||
) : null}
|
||||
{subtitle ? (
|
||||
<span className="text-body-small-regular-150 font-body-small-regular-150 text-text-neutral-tertiary">
|
||||
{subtitle}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<IconButton variant="tertiary" size="small" icon={<FeatherX />} />
|
||||
</div>
|
||||
<div className="flex w-full flex-col items-start gap-3">
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full flex-col items-start gap-3",
|
||||
{ hidden: hideImport }
|
||||
)}
|
||||
>
|
||||
<div className="flex w-full items-center justify-between rounded-md border border-solid border-border-default bg-surface-neutral-secondary px-3 py-3 gap-3 mobile:flex-wrap">
|
||||
<div className="flex flex-col items-start gap-1">
|
||||
<span className="text-body-default-medium-0 font-body-default-medium-0 text-text-neutral-primary">
|
||||
Aus Kontakt übernehmen
|
||||
</span>
|
||||
<span className="text-body-small-regular-150 font-body-small-regular-150 text-text-neutral-tertiary">
|
||||
Adressdaten aus einem bestehenden Kontakt vorbefüllen
|
||||
</span>
|
||||
</div>
|
||||
<SubframeCore.DropdownMenu.Root>
|
||||
<SubframeCore.DropdownMenu.Trigger asChild={true}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="small"
|
||||
icon={<FeatherUserSearch />}
|
||||
iconRight={<FeatherChevronDown />}
|
||||
>
|
||||
Kontakt suchen
|
||||
</Button>
|
||||
</SubframeCore.DropdownMenu.Trigger>
|
||||
<SubframeCore.DropdownMenu.Portal>
|
||||
<SubframeCore.DropdownMenu.Content
|
||||
side="bottom"
|
||||
align="end"
|
||||
sideOffset={4}
|
||||
asChild={true}
|
||||
>
|
||||
<div className="flex w-80 flex-none flex-col items-start overflow-hidden rounded-md border border-solid border-border-default bg-surface-neutral-secondary shadow-lg">
|
||||
<div className="flex w-full flex-col items-start px-2 py-2">
|
||||
<SearchInput
|
||||
className="h-9 w-full flex-none"
|
||||
size="small"
|
||||
fillContainer={true}
|
||||
label="Label"
|
||||
helpText="Help text"
|
||||
message="Hilfetext"
|
||||
hideShortcut={true}
|
||||
>
|
||||
<SearchInput.Input placeholder="Kontakt oder Firma suchen" />
|
||||
</SearchInput>
|
||||
</div>
|
||||
<div className="flex h-px w-full flex-none flex-col items-start bg-border-default" />
|
||||
<div className="flex w-full flex-col items-start px-1 py-1">
|
||||
<div className="flex w-full items-center gap-3 rounded-md px-2 py-2 cursor-pointer hover:bg-surface-neutral-tertiary">
|
||||
<Avatar size="small">MB</Avatar>
|
||||
<div className="flex grow shrink-0 basis-0 flex-col items-start">
|
||||
<span className="line-clamp-1 w-full text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-primary">
|
||||
Markus Bauer
|
||||
</span>
|
||||
<span className="line-clamp-1 w-full text-body-small-regular-150 font-body-small-regular-150 text-text-neutral-tertiary">
|
||||
Bauer Elektrotechnik GmbH · Berlin
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full items-center gap-3 rounded-md px-2 py-2 cursor-pointer hover:bg-surface-neutral-tertiary">
|
||||
<Avatar size="small">SK</Avatar>
|
||||
<div className="flex grow shrink-0 basis-0 flex-col items-start">
|
||||
<span className="line-clamp-1 w-full text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-primary">
|
||||
Sandra Keller
|
||||
</span>
|
||||
<span className="line-clamp-1 w-full text-body-small-regular-150 font-body-small-regular-150 text-text-neutral-tertiary">
|
||||
Keller Immobilien · Potsdam
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full items-center gap-3 rounded-md px-2 py-2 cursor-pointer hover:bg-surface-neutral-tertiary">
|
||||
<Avatar size="small">JR</Avatar>
|
||||
<div className="flex grow shrink-0 basis-0 flex-col items-start">
|
||||
<span className="line-clamp-1 w-full text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-primary">
|
||||
Jonas Richter
|
||||
</span>
|
||||
<span className="line-clamp-1 w-full text-body-small-regular-150 font-body-small-regular-150 text-text-neutral-tertiary">
|
||||
Richter Bau AG · Hamburg
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full items-center gap-3 rounded-md px-2 py-2 cursor-pointer hover:bg-surface-neutral-tertiary">
|
||||
<Avatar size="small">EV</Avatar>
|
||||
<div className="flex grow shrink-0 basis-0 flex-col items-start">
|
||||
<span className="line-clamp-1 w-full text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-primary">
|
||||
Elena Vogt
|
||||
</span>
|
||||
<span className="line-clamp-1 w-full text-body-small-regular-150 font-body-small-regular-150 text-text-neutral-tertiary">
|
||||
— · München
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex h-px w-full flex-none flex-col items-start bg-border-default" />
|
||||
<div className="flex w-full items-center gap-2 px-3 py-2.5 cursor-pointer hover:bg-surface-neutral-tertiary">
|
||||
<span className="grow shrink-0 basis-0 text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-secondary">
|
||||
Alle Kontakte durchsuchen
|
||||
</span>
|
||||
<FeatherArrowRight className="text-body-small-regular-150 font-body-small-regular-150 text-icon-neutral-tertiary" />
|
||||
</div>
|
||||
</div>
|
||||
</SubframeCore.DropdownMenu.Content>
|
||||
</SubframeCore.DropdownMenu.Portal>
|
||||
</SubframeCore.DropdownMenu.Root>
|
||||
</div>
|
||||
<div className="flex h-px w-full flex-none flex-col items-start bg-border-default" />
|
||||
</div>
|
||||
<Select
|
||||
className={SubframeUtils.twClassNames({
|
||||
hidden: hideAddressType,
|
||||
})}
|
||||
variant="outline"
|
||||
fillContainer={true}
|
||||
showLabel={true}
|
||||
label="Adresstyp"
|
||||
placeholder="Lieferadresse"
|
||||
helpText=""
|
||||
errorText=""
|
||||
message=""
|
||||
>
|
||||
<Select.Item
|
||||
value="Lieferadresse"
|
||||
showItemText={true}
|
||||
content={
|
||||
<span className="grow shrink-0 basis-0 text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-tertiary">
|
||||
Option
|
||||
</span>
|
||||
}
|
||||
>
|
||||
Lieferadresse
|
||||
</Select.Item>
|
||||
<Select.Item
|
||||
value="Baustelle"
|
||||
showItemText={true}
|
||||
content={
|
||||
<span className="grow shrink-0 basis-0 text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-tertiary">
|
||||
Option
|
||||
</span>
|
||||
}
|
||||
>
|
||||
Baustelle
|
||||
</Select.Item>
|
||||
<Select.Item
|
||||
value="Rechnungsadresse"
|
||||
showItemText={true}
|
||||
content={
|
||||
<span className="grow shrink-0 basis-0 text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-tertiary">
|
||||
Option
|
||||
</span>
|
||||
}
|
||||
>
|
||||
Rechnungsadresse
|
||||
</Select.Item>
|
||||
<Select.Item
|
||||
value="Büro"
|
||||
showItemText={true}
|
||||
content={
|
||||
<span className="grow shrink-0 basis-0 text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-tertiary">
|
||||
Option
|
||||
</span>
|
||||
}
|
||||
>
|
||||
Büro
|
||||
</Select.Item>
|
||||
<Select.Item
|
||||
value="Privat"
|
||||
showItemText={true}
|
||||
content={
|
||||
<span className="grow shrink-0 basis-0 text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-tertiary">
|
||||
Option
|
||||
</span>
|
||||
}
|
||||
>
|
||||
Privat
|
||||
</Select.Item>
|
||||
</Select>
|
||||
<TextInput
|
||||
className={SubframeUtils.twClassNames("h-auto w-full flex-none", {
|
||||
hidden: hideCompany,
|
||||
})}
|
||||
fillContainer={true}
|
||||
showLabel={true}
|
||||
label="Firma"
|
||||
helpText=""
|
||||
message=""
|
||||
>
|
||||
<TextInput.Input placeholder="Firmenname (optional)" value="" />
|
||||
</TextInput>
|
||||
<TextInput
|
||||
className="h-auto w-full flex-none"
|
||||
fillContainer={true}
|
||||
showLabel={true}
|
||||
label="Name / Ansprechpartner"
|
||||
helpText=""
|
||||
message=""
|
||||
>
|
||||
<TextInput.Input placeholder="Vor- und Nachname" value="" />
|
||||
</TextInput>
|
||||
<div className="flex w-full items-end gap-3 mobile:flex-wrap">
|
||||
<div className="flex grow shrink-0 basis-0 flex-col items-start">
|
||||
<TextInput
|
||||
className="h-auto w-full flex-none"
|
||||
fillContainer={true}
|
||||
showLabel={true}
|
||||
label="Straße"
|
||||
helpText=""
|
||||
message=""
|
||||
>
|
||||
<TextInput.Input placeholder="Straßenname" value="" />
|
||||
</TextInput>
|
||||
</div>
|
||||
<div className="flex w-48 flex-none flex-col items-start">
|
||||
<TextInput
|
||||
className="h-auto w-full flex-none"
|
||||
fillContainer={true}
|
||||
showLabel={true}
|
||||
label="Nr."
|
||||
helpText=""
|
||||
message=""
|
||||
>
|
||||
<TextInput.Input placeholder="12a" value="" />
|
||||
</TextInput>
|
||||
</div>
|
||||
</div>
|
||||
<TextInput
|
||||
className="h-auto w-full flex-none"
|
||||
fillContainer={true}
|
||||
showLabel={true}
|
||||
label="Adresszusatz"
|
||||
helpText=""
|
||||
message=""
|
||||
>
|
||||
<TextInput.Input
|
||||
placeholder="Etage, Gebäude, c/o (optional)"
|
||||
value=""
|
||||
/>
|
||||
</TextInput>
|
||||
<div className="flex w-full items-end gap-3 mobile:flex-wrap">
|
||||
<div className="flex w-48 flex-none flex-col items-start">
|
||||
<TextInput
|
||||
className="h-auto w-full flex-none"
|
||||
fillContainer={true}
|
||||
showLabel={true}
|
||||
label="PLZ"
|
||||
helpText=""
|
||||
message=""
|
||||
>
|
||||
<TextInput.Input placeholder="10115" value="" />
|
||||
</TextInput>
|
||||
</div>
|
||||
<div className="flex grow shrink-0 basis-0 flex-col items-start">
|
||||
<TextInput
|
||||
className="h-auto w-full flex-none"
|
||||
fillContainer={true}
|
||||
showLabel={true}
|
||||
label="Ort"
|
||||
helpText=""
|
||||
message=""
|
||||
>
|
||||
<TextInput.Input placeholder="Berlin" value="" />
|
||||
</TextInput>
|
||||
</div>
|
||||
</div>
|
||||
<Select
|
||||
variant="outline"
|
||||
fillContainer={true}
|
||||
showLabel={true}
|
||||
label="Land"
|
||||
placeholder="Deutschland"
|
||||
helpText=""
|
||||
errorText=""
|
||||
message=""
|
||||
>
|
||||
<Select.Item
|
||||
value="Deutschland"
|
||||
showItemText={true}
|
||||
content={
|
||||
<span className="grow shrink-0 basis-0 text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-tertiary">
|
||||
Option
|
||||
</span>
|
||||
}
|
||||
>
|
||||
Deutschland
|
||||
</Select.Item>
|
||||
<Select.Item
|
||||
value="Österreich"
|
||||
showItemText={true}
|
||||
content={
|
||||
<span className="grow shrink-0 basis-0 text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-tertiary">
|
||||
Option
|
||||
</span>
|
||||
}
|
||||
>
|
||||
Österreich
|
||||
</Select.Item>
|
||||
<Select.Item
|
||||
value="Schweiz"
|
||||
showItemText={true}
|
||||
content={
|
||||
<span className="grow shrink-0 basis-0 text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-tertiary">
|
||||
Option
|
||||
</span>
|
||||
}
|
||||
>
|
||||
Schweiz
|
||||
</Select.Item>
|
||||
<Select.Item
|
||||
value="Niederlande"
|
||||
showItemText={true}
|
||||
content={
|
||||
<span className="grow shrink-0 basis-0 text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-tertiary">
|
||||
Option
|
||||
</span>
|
||||
}
|
||||
>
|
||||
Niederlande
|
||||
</Select.Item>
|
||||
<Select.Item
|
||||
value="Polen"
|
||||
showItemText={true}
|
||||
content={
|
||||
<span className="grow shrink-0 basis-0 text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-tertiary">
|
||||
Option
|
||||
</span>
|
||||
}
|
||||
>
|
||||
Polen
|
||||
</Select.Item>
|
||||
</Select>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full flex-col items-start gap-3",
|
||||
{ hidden: hideNote }
|
||||
)}
|
||||
>
|
||||
<div className="flex h-px w-full flex-none flex-col items-start bg-border-default" />
|
||||
<TextArea
|
||||
className="h-auto w-full flex-none max-w-[9999px]"
|
||||
error={false}
|
||||
fillContainer={false}
|
||||
showLabel={true}
|
||||
label="Notiz zur Adresse"
|
||||
helpText="Nur intern sichtbar, wird nicht auf Versandlabels gedruckt"
|
||||
errorText=""
|
||||
message=""
|
||||
>
|
||||
<TextArea.Input
|
||||
className="h-16 w-full flex-none min-h-[64px]"
|
||||
placeholder="z. B. Zufahrt über den Hinterhof, Schlüssel beim Nachbarn, Parkmöglichkeit"
|
||||
value=""
|
||||
/>
|
||||
</TextArea>
|
||||
</div>
|
||||
<Checkbox
|
||||
className={SubframeUtils.twClassNames({
|
||||
hidden: hideDefaultAddress,
|
||||
})}
|
||||
label="Als Standardadresse verwenden"
|
||||
description=""
|
||||
helpText=""
|
||||
/>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-end gap-2 bg-surface-neutral-secondary pt-2 sticky bottom-0">
|
||||
<Button variant="tertiary">Abbrechen</Button>
|
||||
<Button variant="primary" icon={<FeatherCheck />}>
|
||||
Adresse speichern
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
</Dialog>
|
||||
);
|
||||
});
|
||||
|
||||
export const AddressDialog = AddressDialogRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { AddressDialog as AddressDialogComponent } from "./AddressDialog";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const AddressDialog = AddressDialogComponent;
|
||||
@@ -0,0 +1,153 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Alert — https://app.subframe.com/f023064a5ca0/library?component=Alert_3a65613d-d546-467c-80f4-aaba6a7edcd5
|
||||
* Icon Button — https://app.subframe.com/f023064a5ca0/library?component=Icon+Button_af9405b1-8c54-4e01-9786-5aad308224f6
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherAlertCircle } from "@subframe/core";
|
||||
import { FeatherAlertTriangle } from "@subframe/core";
|
||||
import { FeatherCheckCircle } from "@subframe/core";
|
||||
import { FeatherInbox } from "@subframe/core";
|
||||
import { FeatherInfo } from "@subframe/core";
|
||||
import { FeatherX } from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
import { IconButton } from "../IconButton";
|
||||
|
||||
export interface AlertRootProps
|
||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
||||
variant?: "information" | "success" | "warning" | "error" | "neutral";
|
||||
notDismissible?: boolean;
|
||||
title?: React.ReactNode;
|
||||
description?: React.ReactNode;
|
||||
textSize?: "small" | "medium";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const AlertRoot = React.forwardRef<HTMLDivElement, AlertRootProps>(
|
||||
function AlertRoot(
|
||||
{
|
||||
variant = "information",
|
||||
notDismissible = false,
|
||||
title,
|
||||
description,
|
||||
textSize = "medium",
|
||||
className,
|
||||
...otherProps
|
||||
}: AlertRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/3a65613d flex w-full flex-col items-start gap-2 rounded-md border border-solid border-border-information-subtle bg-surface-feedback-subtle-information px-4 py-5 relative",
|
||||
{
|
||||
"px-3 py-3.5": textSize === "small",
|
||||
"border border-solid border-border-default bg-surface-feedback-subtle-neutral":
|
||||
variant === "neutral",
|
||||
"border border-solid border-border-negative-subtle bg-surface-feedback-subtle-negative":
|
||||
variant === "error",
|
||||
"border border-solid border-border-warning-subtle bg-surface-feedback-subtle-warning":
|
||||
variant === "warning",
|
||||
"border border-solid border-border-positive-subtle bg-surface-feedback-subtle-positive":
|
||||
variant === "success",
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full items-start gap-3",
|
||||
{ "gap-2.5": textSize === "small" }
|
||||
)}
|
||||
>
|
||||
<FeatherInfo
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-heading-0 font-heading-0 text-icon-feedback-information",
|
||||
{
|
||||
"text-body-large font-body-large": textSize === "small",
|
||||
hidden:
|
||||
variant === "neutral" ||
|
||||
variant === "error" ||
|
||||
variant === "warning" ||
|
||||
variant === "success",
|
||||
}
|
||||
)}
|
||||
/>
|
||||
<FeatherCheckCircle
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden text-heading-0 font-heading-0 text-icon-feedback-positive",
|
||||
{ "inline-flex": variant === "success" }
|
||||
)}
|
||||
/>
|
||||
<FeatherAlertTriangle
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden text-heading-0 font-heading-0 text-icon-feedback-warning",
|
||||
{ "inline-flex": variant === "warning" }
|
||||
)}
|
||||
/>
|
||||
<FeatherAlertCircle
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden text-heading-0 font-heading-0 text-icon-feedback-negative",
|
||||
{ "inline-flex": variant === "error" }
|
||||
)}
|
||||
/>
|
||||
<FeatherInbox
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden text-heading-0 font-heading-0 text-icon-neutral-secondary",
|
||||
{ "inline-flex": variant === "neutral" }
|
||||
)}
|
||||
/>
|
||||
<div className="flex grow shrink-0 basis-0 flex-col items-start gap-2">
|
||||
{title ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"w-full whitespace-pre-wrap text-body-default-medium-0 font-body-default-medium-0 text-text-feedback-information",
|
||||
{
|
||||
"text-body-small-medium-0 font-body-small-medium-0":
|
||||
textSize === "small",
|
||||
"text-text-neutral-primary": variant === "neutral",
|
||||
"text-text-feedback-negative": variant === "error",
|
||||
"text-text-feedback-warning": variant === "warning",
|
||||
"text-text-feedback-positive": variant === "success",
|
||||
}
|
||||
)}
|
||||
>
|
||||
{title}
|
||||
</span>
|
||||
) : null}
|
||||
{description ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"w-full whitespace-pre-wrap text-body-default-regular-150 font-body-default-regular-150 text-text-neutral-secondary",
|
||||
{
|
||||
"text-body-small-regular-150 font-body-small-regular-150":
|
||||
textSize === "small",
|
||||
}
|
||||
)}
|
||||
>
|
||||
{description}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex items-center justify-end gap-1 px-1 py-1",
|
||||
{ hidden: notDismissible }
|
||||
)}
|
||||
>
|
||||
<IconButton variant="ghost" square={true} icon={<FeatherX />} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden items-start justify-center absolute top-3 right-3">
|
||||
<FeatherX className="text-body-default-regular-0 font-body-default-regular-0 text-icon-neutral-secondary" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const Alert = AlertRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { Alert as AlertComponent } from "./Alert";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const Alert = AlertComponent;
|
||||
@@ -0,0 +1,42 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Area Chart — https://app.subframe.com/f023064a5ca0/library?component=Area+Chart_8aa1e7b3-5db6-4a62-aa49-137ced21a231
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface AreaChartRootProps
|
||||
extends React.ComponentProps<typeof SubframeCore.AreaChart> {
|
||||
stacked?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const AreaChartRoot = React.forwardRef<
|
||||
React.ElementRef<typeof SubframeCore.AreaChart>,
|
||||
AreaChartRootProps
|
||||
>(function AreaChartRoot(
|
||||
{ stacked = false, className, ...otherProps }: AreaChartRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<SubframeCore.AreaChart
|
||||
className={SubframeUtils.twClassNames("h-80 w-full", className)}
|
||||
ref={ref}
|
||||
stacked={stacked}
|
||||
colors={[
|
||||
"#3b82f6",
|
||||
"#bfdbfe",
|
||||
"#2563eb",
|
||||
"#93c5fd",
|
||||
"#1d4ed8",
|
||||
"#60a5fa",
|
||||
]}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const AreaChart = AreaChartRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { AreaChart as AreaChartComponent } from "./AreaChart";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const AreaChart = AreaChartComponent;
|
||||
@@ -0,0 +1,108 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Avatar — https://app.subframe.com/f023064a5ca0/library?component=Avatar_bec25ae6-5010-4485-b46b-cf79e3943ab2
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface AvatarRootProps
|
||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, "color"> {
|
||||
color?: "neutral" | "blue" | "green" | "red" | "yellow" | "purple";
|
||||
size?:
|
||||
| "extra-small"
|
||||
| "small"
|
||||
| "medium"
|
||||
| "large"
|
||||
| "extra-large"
|
||||
| "2-x-large"
|
||||
| "3-x-large";
|
||||
border?: boolean;
|
||||
children?: React.ReactNode;
|
||||
image?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const AvatarRoot = React.forwardRef<HTMLDivElement, AvatarRootProps>(
|
||||
function AvatarRoot(
|
||||
{
|
||||
color = "neutral",
|
||||
size = "medium",
|
||||
border = false,
|
||||
children,
|
||||
image,
|
||||
className,
|
||||
...otherProps
|
||||
}: AvatarRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/bec25ae6 flex h-10 w-10 flex-col items-center justify-center gap-2 overflow-hidden rounded-round bg-surface-neutral-tertiary relative",
|
||||
{
|
||||
"border border-solid border-border-on-primary": border,
|
||||
"h-32 w-32": size === "3-x-large",
|
||||
"h-20 w-20": size === "2-x-large",
|
||||
"h-16 w-16": size === "extra-large",
|
||||
"h-12 w-12": size === "large",
|
||||
"h-8 w-8": size === "small",
|
||||
"h-6 w-6": size === "extra-small",
|
||||
"bg-surface-ui-subtle-purple-default": color === "purple",
|
||||
"bg-surface-ui-subtle-yellow-default": color === "yellow",
|
||||
"bg-surface-ui-subtle-red-default": color === "red",
|
||||
"bg-surface-ui-subtle-green-default": color === "green",
|
||||
"bg-surface-ui-subtle-blue-default": color === "blue",
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{children ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"line-clamp-1 w-full text-body-default-medium-150 font-body-default-medium-150 text-text-neutral-secondary text-center absolute",
|
||||
{
|
||||
"text-title-hero font-title-hero": size === "3-x-large",
|
||||
"text-title-page font-title-page": size === "2-x-large",
|
||||
"text-subtitle font-subtitle": size === "extra-large",
|
||||
"text-heading-0 font-heading-0": size === "large",
|
||||
"text-body-small-medium-150 font-body-small-medium-150":
|
||||
size === "small",
|
||||
"text-body-extra-small-medium-150 font-body-extra-small-medium-150":
|
||||
size === "extra-small",
|
||||
"text-text-ui-purple": color === "purple",
|
||||
"text-text-ui-yellow": color === "yellow",
|
||||
"text-text-ui-red": color === "red",
|
||||
"text-text-ui-green": color === "green",
|
||||
"text-text-ui-blue": color === "blue",
|
||||
}
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
{image ? (
|
||||
<img
|
||||
className={SubframeUtils.twClassNames(
|
||||
"h-10 w-10 flex-none object-cover absolute",
|
||||
{
|
||||
"h-24 w-24": size === "3-x-large",
|
||||
"h-20 w-20": size === "2-x-large",
|
||||
"h-16 w-16": size === "extra-large",
|
||||
"h-12 w-12": size === "large",
|
||||
"h-8 w-8": size === "small",
|
||||
"h-6 w-6": size === "extra-small",
|
||||
}
|
||||
)}
|
||||
src={image}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const Avatar = AvatarRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { Avatar as AvatarComponent } from "./Avatar";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const Avatar = AvatarComponent;
|
||||
@@ -0,0 +1,110 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Avatar Group — https://app.subframe.com/f023064a5ca0/library?component=Avatar+Group_6e7444ab-1d67-47f5-af23-1a2d3ecf1016
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface AvatarOverflowProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
border?: boolean;
|
||||
overflowLabel?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const AvatarOverflow = React.forwardRef<HTMLDivElement, AvatarOverflowProps>(
|
||||
function AvatarOverflow(
|
||||
{
|
||||
border = false,
|
||||
overflowLabel,
|
||||
className,
|
||||
...otherProps
|
||||
}: AvatarOverflowProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/dbd63208 flex h-10 w-10 cursor-pointer items-center justify-center overflow-hidden rounded-round bg-surface-neutral-tertiary group/avataroverflow shrink-0 relative",
|
||||
{ "border border-solid border-border-on-primary": border },
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{overflowLabel ? (
|
||||
<span className="text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-secondary">
|
||||
{overflowLabel}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export interface AvatarGroupRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
hoverEffect?: "on" | "off";
|
||||
border?: boolean;
|
||||
showOverflow?: boolean;
|
||||
children?: React.ReactNode;
|
||||
overflowLabel?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const AvatarGroupRoot = React.forwardRef<HTMLDivElement, AvatarGroupRootProps>(
|
||||
function AvatarGroupRoot(
|
||||
{
|
||||
hoverEffect = "off",
|
||||
border = false,
|
||||
showOverflow = false,
|
||||
children,
|
||||
overflowLabel,
|
||||
className,
|
||||
...otherProps
|
||||
}: AvatarGroupRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/6e7444ab items-center group/mtavatargroup inline-flex [&>*:not(:first-child)]:-ml-2",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{children ? (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"items-center inline-flex [&>*:not(:first-child)]:-ml-2 [&>*]:relative relative [&>*]:z-0 z-0 [&>*:nth-child(1)]:z-[1] [&>*:nth-child(2)]:z-[2] [&>*:nth-child(3)]:z-[3] [&>*:nth-child(4)]:z-[4] [&>*:nth-child(5)]:z-[5]",
|
||||
{
|
||||
"[&>*]:border [&>*]:border-solid [&>*]:border-border-on-primary":
|
||||
border,
|
||||
"[&>*]:transition-transform [&>*]:duration-200 [&>*:hover]:scale-110 [&>*:hover]:rotate-6 [&>*:hover]:z-20":
|
||||
hoverEffect === "on",
|
||||
}
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
) : null}
|
||||
<AvatarOverflow
|
||||
className={SubframeUtils.twClassNames("hidden relative z-10", {
|
||||
flex: showOverflow,
|
||||
"border border-solid border-border-on-primary": border,
|
||||
"transition-transform duration-200 hover:scale-110 hover:rotate-6":
|
||||
hoverEffect === "on",
|
||||
})}
|
||||
overflowLabel={overflowLabel}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const AvatarGroup = Object.assign(AvatarGroupRoot, {
|
||||
AvatarOverflow,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { AvatarGroup as AvatarGroupComponent } from "./AvatarGroup";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const AvatarGroup = AvatarGroupComponent;
|
||||
@@ -0,0 +1,317 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Badge — https://app.subframe.com/f023064a5ca0/library?component=Badge_7d75e221-355a-4695-acb7-3902a9f6ffde
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface BadgeRootProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
tone?:
|
||||
| "red-subtle"
|
||||
| "orange-subtle"
|
||||
| "yellow-subtle"
|
||||
| "pear-subtle"
|
||||
| "lime-subtle"
|
||||
| "green-subtle"
|
||||
| "teal-subtle"
|
||||
| "ocean-subtle"
|
||||
| "sky-subtle"
|
||||
| "blue-subtle"
|
||||
| "indigo-subtle"
|
||||
| "violet-subtle"
|
||||
| "fuchsia-subtle"
|
||||
| "pink-subtle"
|
||||
| "sand-subtle"
|
||||
| "brown-subtle"
|
||||
| "red-solid"
|
||||
| "orange-solid"
|
||||
| "yellow-solid"
|
||||
| "pear-solid"
|
||||
| "lime-solid"
|
||||
| "green-solid"
|
||||
| "teal-solid"
|
||||
| "ocean-solid"
|
||||
| "sky-solid"
|
||||
| "blue-solid"
|
||||
| "indigo-solid"
|
||||
| "violet-solid"
|
||||
| "fuchsia-solid"
|
||||
| "pink-solid"
|
||||
| "sand-solid"
|
||||
| "brown-solid"
|
||||
| "red-outline"
|
||||
| "orange-outline"
|
||||
| "yellow-outline"
|
||||
| "pear-outline"
|
||||
| "lime-outline"
|
||||
| "green-outline"
|
||||
| "teal-outline"
|
||||
| "ocean-outline"
|
||||
| "sky-outline"
|
||||
| "blue-outline"
|
||||
| "indigo-outline"
|
||||
| "violet-outline"
|
||||
| "fuchsia-outline"
|
||||
| "pink-outline"
|
||||
| "sand-outline"
|
||||
| "brown-outline"
|
||||
| "brand-primary-solid"
|
||||
| "brand-secondary-solid"
|
||||
| "neutral-solid"
|
||||
| "brand-primary-subtle"
|
||||
| "brand-secondary-subtle"
|
||||
| "neutral-subtle"
|
||||
| "white";
|
||||
children?: React.ReactNode;
|
||||
size?: "small" | "medium" | "large";
|
||||
hideDot?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const BadgeRoot = React.forwardRef<HTMLDivElement, BadgeRootProps>(
|
||||
function BadgeRoot(
|
||||
{
|
||||
tone = "neutral-subtle",
|
||||
children,
|
||||
size = "medium",
|
||||
hideDot = false,
|
||||
className,
|
||||
...otherProps
|
||||
}: BadgeRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/7d75e221 flex h-6 items-center gap-2 rounded-round border border-solid border-border-default bg-surface-feedback-subtle-neutral px-2.5 py-1",
|
||||
{
|
||||
"h-8 py-2": size === "large",
|
||||
"h-5 py-0.5": size === "small",
|
||||
"bg-surface-neutral-secondary": tone === "white",
|
||||
"border border-solid border-brand-brand-secondary-surface-subtle-default bg-brand-brand-secondary-surface-subtle-default":
|
||||
tone === "brand-secondary-subtle",
|
||||
"border border-solid border-brand-brand-surface-subtle-default bg-brand-brand-surface-subtle-default":
|
||||
tone === "brand-primary-subtle",
|
||||
"border border-solid border-surface-feedback-solid-neutral bg-surface-feedback-solid-neutral":
|
||||
tone === "neutral-solid",
|
||||
"border border-solid border-brand-brand-secondary-surface-solid-default bg-brand-brand-secondary-surface-solid-default":
|
||||
tone === "brand-secondary-solid",
|
||||
"border border-solid border-brand-brand-surface-solid-default bg-brand-brand-surface-solid-default":
|
||||
tone === "brand-primary-solid",
|
||||
"border-2 border-solid border-[#c2a682] bg-[#fafafa]":
|
||||
tone === "brown-outline",
|
||||
"border-2 border-solid border-[#c8baa9] bg-[#fafafa]":
|
||||
tone === "sand-outline",
|
||||
"border-2 border-solid border-[#f9a8d4] bg-[#fafafa]":
|
||||
tone === "pink-outline",
|
||||
"border-2 border-solid border-[#f0abfc] bg-[#fafafa]":
|
||||
tone === "fuchsia-outline",
|
||||
"border-2 border-solid border-[#cfb2ff] bg-[#fafafa]":
|
||||
tone === "violet-outline",
|
||||
"border-2 border-solid border-[#b3b8ff] bg-[#fafafa]":
|
||||
tone === "indigo-outline",
|
||||
"border-2 border-solid border-[#85c7ff] bg-[#fafafa]":
|
||||
tone === "blue-outline",
|
||||
"border-2 border-solid border-[#79c7ff] bg-[#fafafa]":
|
||||
tone === "sky-outline",
|
||||
"border-2 border-solid border-[#81dbf8] bg-[#fafafa]":
|
||||
tone === "ocean-outline",
|
||||
"border-2 border-solid border-[#51f7e5] bg-[#fafafa]":
|
||||
tone === "teal-outline",
|
||||
"border-2 border-solid border-[#85f0a3] bg-[#fafafa]":
|
||||
tone === "green-outline",
|
||||
"border-2 border-solid border-[#bef264] bg-[#fafafa]":
|
||||
tone === "lime-outline",
|
||||
"border-2 border-solid border-[#e0eb6b] bg-[#fafafa]":
|
||||
tone === "pear-outline",
|
||||
"border-2 border-solid border-[#f6d84e] bg-[#fafafa]":
|
||||
tone === "yellow-outline",
|
||||
"border-2 border-solid border-[#ffa570] bg-[#fafafa]":
|
||||
tone === "orange-outline",
|
||||
"border-2 border-solid border-[#fba6a6] bg-[#fafafa]":
|
||||
tone === "red-outline",
|
||||
"border border-solid border-[#8a5f44] bg-[#8a5f44]":
|
||||
tone === "brown-solid",
|
||||
"border border-solid border-[#947562] bg-[#947562]":
|
||||
tone === "sand-solid",
|
||||
"border border-solid border-[#db2777] bg-[#db2777]":
|
||||
tone === "pink-solid",
|
||||
"border border-solid border-[#c026d3] bg-[#c026d3]":
|
||||
tone === "fuchsia-solid",
|
||||
"border border-solid border-[#7c2df0] bg-[#7c2df0]":
|
||||
tone === "violet-solid",
|
||||
"border border-solid border-[#4835f2] bg-[#4835f2]":
|
||||
tone === "indigo-solid",
|
||||
"border border-solid border-[#0c4bff] bg-[#0c4bff]":
|
||||
tone === "blue-solid",
|
||||
"border border-solid border-[#0071ce] bg-[#0071ce]":
|
||||
tone === "sky-solid",
|
||||
"border border-solid border-[#0890c1] bg-[#0890c1]":
|
||||
tone === "ocean-solid",
|
||||
"border border-solid border-[#05807c] bg-[#05807c]":
|
||||
tone === "teal-solid",
|
||||
"border border-solid border-[#15a43e] bg-[#15a43e]":
|
||||
tone === "green-solid",
|
||||
"border border-solid border-[#4d7c0f] bg-[#4d7c0f]":
|
||||
tone === "lime-solid",
|
||||
"border border-solid border-[#677615] bg-[#677615]":
|
||||
tone === "pear-solid",
|
||||
"border border-solid border-[#e0c200] bg-[#e0c200]":
|
||||
tone === "yellow-solid",
|
||||
"border border-solid border-[#ea580c] bg-[#ea580c]":
|
||||
tone === "orange-solid",
|
||||
"border border-solid border-[#da2828] bg-[#da2828]":
|
||||
tone === "red-solid",
|
||||
"border border-solid border-[#d9c8af] bg-[#ebe3d6]":
|
||||
tone === "brown-subtle",
|
||||
"border border-solid border-[#ded6ca] bg-[#ede9e3]":
|
||||
tone === "sand-subtle",
|
||||
"border border-solid border-[#fbcfe8] bg-[#fce7f3]":
|
||||
tone === "pink-subtle",
|
||||
"border border-solid border-[#f5d0fe] bg-[#fae8ff]":
|
||||
tone === "fuchsia-subtle",
|
||||
"border border-solid border-[#e4d4ff] bg-[#f0e7ff]":
|
||||
tone === "violet-subtle",
|
||||
"border border-solid border-[#d5d9ff] bg-[#e8e9ff]":
|
||||
tone === "indigo-subtle",
|
||||
"border border-solid border-[#b3dfff] bg-[#d5efff]":
|
||||
tone === "blue-subtle",
|
||||
"border border-solid border-border-information-subtle bg-surface-feedback-subtle-information":
|
||||
tone === "sky-subtle",
|
||||
"border border-solid border-[#bceafb] bg-[#e1f4fd]":
|
||||
tone === "ocean-subtle",
|
||||
"border border-solid border-[#90ffee] bg-[#c8fff7]":
|
||||
tone === "teal-subtle",
|
||||
"border border-solid border-[#baf8cb] bg-[#dcfce4]":
|
||||
tone === "green-subtle",
|
||||
"border border-solid border-[#d9f99d] bg-[#ecfccb]":
|
||||
tone === "lime-subtle",
|
||||
"border border-solid border-[#eff4a2] bg-[#f8facd]":
|
||||
tone === "pear-subtle",
|
||||
"border border-solid border-[#faeb8e] bg-[#fcf6c5]":
|
||||
tone === "yellow-subtle",
|
||||
"border border-solid border-[#ffcaa8] bg-[#ffe7d4]":
|
||||
tone === "orange-subtle",
|
||||
"border border-solid border-[#fdcbcb] bg-[#fee2e2]":
|
||||
tone === "red-subtle",
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex h-1 w-1 flex-none flex-col items-start rounded-round bg-icon-neutral-on-subtle",
|
||||
{
|
||||
hidden: hideDot,
|
||||
"bg-brand-brand-secondary-surface-solid-default":
|
||||
tone === "brand-secondary-subtle",
|
||||
"bg-brand-brand-surface-solid-default":
|
||||
tone === "brand-primary-subtle",
|
||||
"bg-icon-neutral-on-solid":
|
||||
tone === "neutral-solid" ||
|
||||
tone === "brand-secondary-solid" ||
|
||||
tone === "brand-primary-solid" ||
|
||||
tone === "brown-solid" ||
|
||||
tone === "sand-solid" ||
|
||||
tone === "pink-solid" ||
|
||||
tone === "fuchsia-solid" ||
|
||||
tone === "violet-solid" ||
|
||||
tone === "indigo-solid" ||
|
||||
tone === "blue-solid" ||
|
||||
tone === "sky-solid" ||
|
||||
tone === "ocean-solid" ||
|
||||
tone === "teal-solid" ||
|
||||
tone === "green-solid" ||
|
||||
tone === "lime-solid" ||
|
||||
tone === "pear-solid" ||
|
||||
tone === "yellow-solid" ||
|
||||
tone === "orange-solid" ||
|
||||
tone === "red-solid",
|
||||
"bg-[#6f4a39]": tone === "brown-outline",
|
||||
"bg-[#7c6052]": tone === "sand-outline",
|
||||
"bg-[#be185d]": tone === "pink-outline",
|
||||
"bg-[#a21caf]": tone === "fuchsia-outline",
|
||||
"bg-[#691cd4]": tone === "violet-outline",
|
||||
"bg-[#3923de]": tone === "indigo-outline",
|
||||
"bg-[#003beb]": tone === "blue-outline",
|
||||
"bg-[#0071ce]": tone === "sky-outline" || tone === "sky-subtle",
|
||||
"bg-[#08739c]": tone === "ocean-outline",
|
||||
"bg-[#05807c]": tone === "teal-outline" || tone === "teal-subtle",
|
||||
"bg-[#148134]": tone === "green-outline",
|
||||
"bg-[#4d7c0f]": tone === "lime-outline" || tone === "lime-subtle",
|
||||
"bg-[#677615]": tone === "pear-outline" || tone === "pear-subtle",
|
||||
"bg-[#9b5e0d]": tone === "yellow-outline",
|
||||
"bg-[#c72307]": tone === "orange-outline",
|
||||
"bg-[#c52020]": tone === "red-outline",
|
||||
"bg-[#8a5f44]": tone === "brown-subtle",
|
||||
"bg-[#947562]": tone === "sand-subtle",
|
||||
"bg-[#db2777]": tone === "pink-subtle",
|
||||
"bg-[#c026d3]": tone === "fuchsia-subtle",
|
||||
"bg-[#7c2df0]": tone === "violet-subtle",
|
||||
"bg-[#4835f2]": tone === "indigo-subtle",
|
||||
"bg-[#0c4bff]": tone === "blue-subtle",
|
||||
"bg-[#0890c1]": tone === "ocean-subtle",
|
||||
"bg-[#15a43e]": tone === "green-subtle",
|
||||
"bg-[#e0c200]": tone === "yellow-subtle",
|
||||
"bg-[#ea580c]": tone === "orange-subtle",
|
||||
"bg-[#da2828]": tone === "red-subtle",
|
||||
}
|
||||
)}
|
||||
/>
|
||||
{children ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"whitespace-nowrap text-body-small-medium-0 font-body-small-medium-0 text-text-neutral-on-subtle",
|
||||
{
|
||||
"text-text-neutral-on-solid":
|
||||
tone === "neutral-solid" ||
|
||||
tone === "brand-secondary-solid" ||
|
||||
tone === "brand-primary-solid" ||
|
||||
tone === "brown-solid" ||
|
||||
tone === "sand-solid" ||
|
||||
tone === "pink-solid" ||
|
||||
tone === "fuchsia-solid" ||
|
||||
tone === "violet-solid" ||
|
||||
tone === "indigo-solid" ||
|
||||
tone === "blue-solid" ||
|
||||
tone === "sky-solid" ||
|
||||
tone === "ocean-solid" ||
|
||||
tone === "teal-solid" ||
|
||||
tone === "green-solid" ||
|
||||
tone === "lime-solid" ||
|
||||
tone === "pear-solid" ||
|
||||
tone === "yellow-solid" ||
|
||||
tone === "orange-solid" ||
|
||||
tone === "red-solid",
|
||||
"text-[#6f4a39]": tone === "brown-outline",
|
||||
"text-[#7c6052]": tone === "sand-outline",
|
||||
"text-[#be185d]": tone === "pink-outline",
|
||||
"text-[#a21caf]": tone === "fuchsia-outline",
|
||||
"text-[#691cd4]": tone === "violet-outline",
|
||||
"text-[#3923de]": tone === "indigo-outline",
|
||||
"text-[#003beb]": tone === "blue-outline",
|
||||
"text-[#0071ce]": tone === "sky-outline",
|
||||
"text-[#08739c]": tone === "ocean-outline",
|
||||
"text-[#05807c]": tone === "teal-outline",
|
||||
"text-[#148134]": tone === "green-outline",
|
||||
"text-[#4d7c0f]": tone === "lime-outline",
|
||||
"text-[#677615]": tone === "pear-outline",
|
||||
"text-[#9b5e0d]": tone === "yellow-outline",
|
||||
"text-[#c72307]": tone === "orange-outline",
|
||||
"text-[#c52020]": tone === "red-outline",
|
||||
}
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const Badge = BadgeRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { Badge as BadgeComponent } from "./Badge";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const Badge = BadgeComponent;
|
||||
@@ -0,0 +1,169 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Badge Feedback — https://app.subframe.com/f023064a5ca0/library?component=Badge+Feedback_896531e4-54ea-4e5a-8710-c30fc2ce10fb
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface BadgeFeedbackRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
farbe?:
|
||||
| "solid-neutral"
|
||||
| "solid-positive"
|
||||
| "solid-negative"
|
||||
| "solid-warning"
|
||||
| "solid-information"
|
||||
| "solid-purple"
|
||||
| "solid-blue"
|
||||
| "solid-brand-primary"
|
||||
| "solid-brand-secondary"
|
||||
| "subtle-neutral"
|
||||
| "subtle-positive"
|
||||
| "subtle-negative"
|
||||
| "subtle-warning"
|
||||
| "subtle-information"
|
||||
| "subtle-purple"
|
||||
| "subtle-blue"
|
||||
| "subtle-brand-primary"
|
||||
| "subtle-brand-secondary"
|
||||
| "ghost-neutral"
|
||||
| "ghost-positive"
|
||||
| "ghost-negative"
|
||||
| "ghost-warning"
|
||||
| "ghost-information"
|
||||
| "ghost-purple"
|
||||
| "ghost-blue"
|
||||
| "ghost-brand-primary"
|
||||
| "ghost-brand-secondary";
|
||||
children?: React.ReactNode;
|
||||
size?: "medium" | "small" | "large";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const BadgeFeedbackRoot = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
BadgeFeedbackRootProps
|
||||
>(function BadgeFeedbackRoot(
|
||||
{
|
||||
farbe = "solid-neutral",
|
||||
children,
|
||||
size = "medium",
|
||||
className,
|
||||
...otherProps
|
||||
}: BadgeFeedbackRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/896531e4 flex h-6 items-center gap-2 rounded-round bg-surface-feedback-solid-neutral px-2.5 py-1",
|
||||
{
|
||||
"h-8 py-2": size === "large",
|
||||
"h-5 py-0.5": size === "small",
|
||||
"bg-transparent px-0 py-0":
|
||||
farbe === "ghost-brand-secondary" ||
|
||||
farbe === "ghost-brand-primary" ||
|
||||
farbe === "ghost-blue" ||
|
||||
farbe === "ghost-purple" ||
|
||||
farbe === "ghost-information" ||
|
||||
farbe === "ghost-warning" ||
|
||||
farbe === "ghost-negative" ||
|
||||
farbe === "ghost-positive" ||
|
||||
farbe === "ghost-neutral",
|
||||
"bg-brand-brand-secondary-surface-subtle-default":
|
||||
farbe === "subtle-brand-secondary",
|
||||
"bg-brand-brand-surface-subtle-default":
|
||||
farbe === "subtle-brand-primary",
|
||||
"bg-surface-ui-subtle-blue-default": farbe === "subtle-blue",
|
||||
"bg-surface-ui-subtle-purple-default": farbe === "subtle-purple",
|
||||
"bg-surface-feedback-subtle-information":
|
||||
farbe === "subtle-information",
|
||||
"bg-surface-feedback-subtle-warning": farbe === "subtle-warning",
|
||||
"bg-surface-feedback-subtle-negative": farbe === "subtle-negative",
|
||||
"bg-surface-feedback-subtle-positive": farbe === "subtle-positive",
|
||||
"bg-surface-feedback-subtle-neutral": farbe === "subtle-neutral",
|
||||
"bg-brand-brand-secondary-surface-solid-default":
|
||||
farbe === "solid-brand-secondary",
|
||||
"bg-brand-brand-surface-solid-default":
|
||||
farbe === "solid-brand-primary",
|
||||
"bg-surface-ui-solid-blue-default": farbe === "solid-blue",
|
||||
"bg-surface-ui-solid-purple-default": farbe === "solid-purple",
|
||||
"bg-surface-feedback-solid-information":
|
||||
farbe === "solid-information",
|
||||
"bg-surface-feedback-solid-warning": farbe === "solid-warning",
|
||||
"bg-surface-feedback-solid-negative-default":
|
||||
farbe === "solid-negative",
|
||||
"bg-surface-feedback-solid-positive-default":
|
||||
farbe === "solid-positive",
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex h-1 w-1 flex-none flex-col items-start gap-2 rounded-round bg-icon-neutral-on-solid",
|
||||
{
|
||||
"bg-brand-brand-secondary-surface-solid-default":
|
||||
farbe === "ghost-brand-secondary" ||
|
||||
farbe === "subtle-brand-secondary",
|
||||
"bg-brand-brand-surface-solid-default":
|
||||
farbe === "ghost-brand-primary" ||
|
||||
farbe === "subtle-brand-primary",
|
||||
"bg-icon-ui-blue":
|
||||
farbe === "ghost-blue" || farbe === "subtle-blue",
|
||||
"bg-icon-ui-purple":
|
||||
farbe === "ghost-purple" || farbe === "subtle-purple",
|
||||
"bg-icon-feedback-information":
|
||||
farbe === "ghost-information" || farbe === "subtle-information",
|
||||
"bg-icon-feedback-warning":
|
||||
farbe === "ghost-warning" || farbe === "subtle-warning",
|
||||
"bg-icon-feedback-negative":
|
||||
farbe === "ghost-negative" || farbe === "subtle-negative",
|
||||
"bg-icon-feedback-positive":
|
||||
farbe === "ghost-positive" || farbe === "subtle-positive",
|
||||
"bg-icon-neutral-on-subtle":
|
||||
farbe === "ghost-neutral" || farbe === "subtle-neutral",
|
||||
}
|
||||
)}
|
||||
/>
|
||||
{children ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"whitespace-nowrap text-body-small-medium-0 font-body-small-medium-0 text-text-neutral-on-solid",
|
||||
{
|
||||
"text-brand-brand-secondary-surface-solid-default":
|
||||
farbe === "ghost-brand-secondary",
|
||||
"text-brand-brand-surface-solid-default":
|
||||
farbe === "ghost-brand-primary",
|
||||
"text-text-ui-blue": farbe === "ghost-blue",
|
||||
"text-text-ui-purple": farbe === "ghost-purple",
|
||||
"text-text-feedback-information": farbe === "ghost-information",
|
||||
"text-text-feedback-warning": farbe === "ghost-warning",
|
||||
"text-text-feedback-negative":
|
||||
farbe === "ghost-negative" || farbe === "subtle-negative",
|
||||
"text-text-feedback-positive":
|
||||
farbe === "ghost-positive" || farbe === "subtle-positive",
|
||||
"text-text-neutral-on-subtle":
|
||||
farbe === "ghost-neutral" ||
|
||||
farbe === "subtle-brand-secondary" ||
|
||||
farbe === "subtle-brand-primary" ||
|
||||
farbe === "subtle-blue" ||
|
||||
farbe === "subtle-purple" ||
|
||||
farbe === "subtle-information" ||
|
||||
farbe === "subtle-warning" ||
|
||||
farbe === "subtle-neutral",
|
||||
}
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const BadgeFeedback = BadgeFeedbackRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { BadgeFeedback as BadgeFeedbackComponent } from "./BadgeFeedback";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const BadgeFeedback = BadgeFeedbackComponent;
|
||||
@@ -0,0 +1,121 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Badge Shortcut — https://app.subframe.com/f023064a5ca0/library?component=Badge+Shortcut_97bdb082-1124-4dd7-a335-b14b822d0157
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherCommand } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface BadgeShortcutRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
variant?:
|
||||
| "neutral"
|
||||
| "brand-primary"
|
||||
| "brand-secondary"
|
||||
| "error"
|
||||
| "warning"
|
||||
| "success";
|
||||
iconLeft1?: React.ReactNode;
|
||||
iconLeft2?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
iconRight?: React.ReactNode;
|
||||
size?: "small" | "medium" | "large";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const BadgeShortcutRoot = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
BadgeShortcutRootProps
|
||||
>(function BadgeShortcutRoot(
|
||||
{
|
||||
variant = "neutral",
|
||||
iconLeft1 = <FeatherCommand />,
|
||||
iconLeft2 = null,
|
||||
children,
|
||||
iconRight = null,
|
||||
size = "medium",
|
||||
className,
|
||||
...otherProps
|
||||
}: BadgeShortcutRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/97bdb082 flex min-h-[20px] items-center justify-center rounded-sm border border-solid border-surface-feedback-subtle-neutral bg-surface-feedback-subtle-neutral px-1 py-0.5",
|
||||
{
|
||||
"min-h-[24px] px-1.5 py-1": size === "large",
|
||||
"min-h-[14px] px-px py-px": size === "small",
|
||||
"border border-solid border-surface-feedback-subtle-positive bg-surface-feedback-subtle-positive":
|
||||
variant === "success",
|
||||
"border border-solid border-surface-feedback-subtle-warning bg-surface-feedback-subtle-warning":
|
||||
variant === "warning",
|
||||
"border border-solid border-surface-feedback-subtle-negative bg-surface-feedback-subtle-negative":
|
||||
variant === "error",
|
||||
"border border-solid border-brand-brand-secondary-surface-subtle-default bg-brand-brand-secondary-surface-subtle-default":
|
||||
variant === "brand-secondary",
|
||||
"border border-solid border-brand-brand-surface-subtle-default bg-brand-brand-surface-subtle-default":
|
||||
variant === "brand-primary",
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{iconLeft1 ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"font-['Instrument_Sans'] text-[14px] font-[400] leading-[13px] text-icon-neutral-on-subtle",
|
||||
{
|
||||
"text-brand-brand-surface-solid-default":
|
||||
variant === "brand-primary",
|
||||
}
|
||||
)}
|
||||
>
|
||||
{iconLeft1}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
{iconLeft2 ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"font-['Instrument_Sans'] text-[14px] font-[400] leading-[13px] text-icon-neutral-on-subtle",
|
||||
{
|
||||
"text-brand-brand-surface-solid-default":
|
||||
variant === "brand-primary",
|
||||
}
|
||||
)}
|
||||
>
|
||||
{iconLeft2}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
{children ? (
|
||||
<span className="whitespace-nowrap font-['Instrument_Sans'] text-[14px] font-[400] leading-[14px] text-text-neutral-on-subtle">
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
{iconRight ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"font-['Inter'] text-[12px] font-[400] leading-[16px] text-icon-neutral-on-subtle",
|
||||
{
|
||||
"text-icon-feedback-positive": variant === "success",
|
||||
"text-icon-feedback-warning": variant === "warning",
|
||||
"text-icon-feedback-negative": variant === "error",
|
||||
"text-brand-brand-secondary-surface-solid-default":
|
||||
variant === "brand-secondary",
|
||||
"text-brand-brand-surface-solid-default":
|
||||
variant === "brand-primary",
|
||||
}
|
||||
)}
|
||||
>
|
||||
{iconRight}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const BadgeShortcut = BadgeShortcutRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { BadgeShortcut as BadgeShortcutComponent } from "./BadgeShortcut";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const BadgeShortcut = BadgeShortcutComponent;
|
||||
@@ -0,0 +1,129 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Banner Alert — https://app.subframe.com/f023064a5ca0/library?component=Banner+Alert_a3e8dcff-3c57-42da-880f-432bb8ce4100
|
||||
* Link Button — https://app.subframe.com/f023064a5ca0/library?component=Link+Button_a4ee726a-774c-4091-8c49-55b659356024
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherAlertCircle } from "@subframe/core";
|
||||
import { FeatherAlertTriangle } from "@subframe/core";
|
||||
import { FeatherCheckCircle } from "@subframe/core";
|
||||
import { FeatherInfo } from "@subframe/core";
|
||||
import { FeatherX } from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
import { LinkButton } from "../LinkButton";
|
||||
|
||||
export interface BannerAlertRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
dismissing?: boolean;
|
||||
type?: "information" | "success" | "warning" | "error";
|
||||
fillContainer?: boolean;
|
||||
hideAction?: boolean;
|
||||
dismissible?: boolean;
|
||||
children?: React.ReactNode;
|
||||
actionLabel?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const BannerAlertRoot = React.forwardRef<HTMLDivElement, BannerAlertRootProps>(
|
||||
function BannerAlertRoot(
|
||||
{
|
||||
dismissing = false,
|
||||
type = "information",
|
||||
fillContainer = false,
|
||||
hideAction = false,
|
||||
dismissible = false,
|
||||
children,
|
||||
actionLabel,
|
||||
className,
|
||||
...otherProps
|
||||
}: BannerAlertRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/a3e8dcff flex w-256 items-center gap-2 border-b border-solid border-border-information-solid bg-surface-feedback-subtle-information px-4 py-3",
|
||||
{
|
||||
"w-full": fillContainer,
|
||||
"border-b border-x-0 border-t-0 border-solid border-border-negative-solid bg-surface-feedback-subtle-negative":
|
||||
type === "error",
|
||||
"border-b border-x-0 border-t-0 border-solid border-border-warning-solid bg-surface-feedback-subtle-warning":
|
||||
type === "warning",
|
||||
"border-b border-x-0 border-t-0 border-solid border-border-positive-solid bg-surface-feedback-subtle-positive":
|
||||
type === "success",
|
||||
hidden: dismissing,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<FeatherInfo
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-large-0 font-body-large-0 text-icon-feedback-information",
|
||||
{
|
||||
hidden:
|
||||
type === "error" || type === "warning" || type === "success",
|
||||
}
|
||||
)}
|
||||
/>
|
||||
<FeatherCheckCircle
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden text-body-large-0 font-body-large-0 text-icon-feedback-positive",
|
||||
{ "inline-flex": type === "success" }
|
||||
)}
|
||||
/>
|
||||
<FeatherAlertTriangle
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden text-body-large-0 font-body-large-0 text-icon-feedback-warning",
|
||||
{ "inline-flex": type === "warning" }
|
||||
)}
|
||||
/>
|
||||
<FeatherAlertCircle
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden text-body-large-0 font-body-large-0 text-icon-feedback-negative",
|
||||
{ "inline-flex": type === "error" }
|
||||
)}
|
||||
/>
|
||||
{children ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"grow shrink-0 basis-0 whitespace-pre-wrap text-body-default-regular-0 font-body-default-regular-0 text-text-feedback-information",
|
||||
{
|
||||
"text-text-feedback-negative": type === "error",
|
||||
"text-text-feedback-warning": type === "warning",
|
||||
"text-text-feedback-positive": type === "success",
|
||||
}
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
<LinkButton iconRight={null} underline={true}>
|
||||
Aktion
|
||||
</LinkButton>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden items-center justify-center rounded-md px-1 py-1 cursor-pointer hover:bg-black-100",
|
||||
{ "inline-flex": dismissible }
|
||||
)}
|
||||
>
|
||||
<FeatherX
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-large-0 font-body-large-0 text-icon-feedback-information",
|
||||
{
|
||||
"text-icon-feedback-negative": type === "error",
|
||||
"text-icon-feedback-warning": type === "warning",
|
||||
"text-icon-feedback-positive": type === "success",
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const BannerAlert = BannerAlertRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { BannerAlert as BannerAlertComponent } from "./BannerAlert";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const BannerAlert = BannerAlertComponent;
|
||||
@@ -0,0 +1,51 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Banner Offline — https://app.subframe.com/f023064a5ca0/library?component=Banner+Offline_2582cb6e-0176-4a5c-b70f-c3bef3c7001d
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface BannerOfflineRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
variant?: "default";
|
||||
text?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const BannerOfflineRoot = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
BannerOfflineRootProps
|
||||
>(function BannerOfflineRoot(
|
||||
{
|
||||
variant = "default",
|
||||
text,
|
||||
className,
|
||||
...otherProps
|
||||
}: BannerOfflineRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full items-center gap-2 border-b border-solid border-border-warning-solid bg-surface-feedback-subtle-warning px-4 py-3",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<img
|
||||
className="h-1 w-1 flex-none object-cover"
|
||||
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNCIgaGVpZ2h0PSI0IiB2aWV3Qm94PSIwIDAgNCA0IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxjaXJjbGUgY3g9IjIiIGN5PSIyIiByPSIyIiBmaWxsPSIjOUI1RTBEIi8+PC9zdmc+"
|
||||
/>
|
||||
{text ? (
|
||||
<span className="grow shrink-0 basis-0 whitespace-pre-wrap text-body-default-regular-0 font-body-default-regular-0 text-text-feedback-warning">
|
||||
{text}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const BannerOffline = BannerOfflineRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { BannerOffline as BannerOfflineComponent } from "./BannerOffline";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const BannerOffline = BannerOfflineComponent;
|
||||
@@ -0,0 +1,42 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Bar Chart — https://app.subframe.com/f023064a5ca0/library?component=Bar+Chart_4d4f30e7-1869-4980-8b96-617df3b37912
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface BarChartRootProps
|
||||
extends React.ComponentProps<typeof SubframeCore.BarChart> {
|
||||
stacked?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const BarChartRoot = React.forwardRef<
|
||||
React.ElementRef<typeof SubframeCore.BarChart>,
|
||||
BarChartRootProps
|
||||
>(function BarChartRoot(
|
||||
{ stacked = false, className, ...otherProps }: BarChartRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<SubframeCore.BarChart
|
||||
className={SubframeUtils.twClassNames("h-80 w-full", className)}
|
||||
ref={ref}
|
||||
stacked={stacked}
|
||||
colors={[
|
||||
"#3b82f6",
|
||||
"#bfdbfe",
|
||||
"#2563eb",
|
||||
"#93c5fd",
|
||||
"#1d4ed8",
|
||||
"#60a5fa",
|
||||
]}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const BarChart = BarChartRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { BarChart as BarChartComponent } from "./BarChart";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const BarChart = BarChartComponent;
|
||||
@@ -0,0 +1,86 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Breadcrumbs — https://app.subframe.com/f023064a5ca0/library?component=Breadcrumbs_8898334b-a66f-4ee8-8bd1-afcfa8e37cc0
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherChevronRight } from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface ItemProps extends React.HTMLAttributes<HTMLSpanElement> {
|
||||
children?: React.ReactNode;
|
||||
active?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Item = React.forwardRef<HTMLSpanElement, ItemProps>(function Item(
|
||||
{ children, active = false, className, ...otherProps }: ItemProps,
|
||||
ref
|
||||
) {
|
||||
return children ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/bbdc1640 line-clamp-1 cursor-pointer break-words text-body-small-regular-0 font-body-small-regular-0 text-text-neutral-tertiary hover:text-text-neutral-primary",
|
||||
{ "text-text-neutral-primary": active },
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
) : null;
|
||||
});
|
||||
|
||||
export interface DividerProps
|
||||
extends React.ComponentProps<typeof FeatherChevronRight> {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Divider = React.forwardRef<
|
||||
React.ElementRef<typeof FeatherChevronRight>,
|
||||
DividerProps
|
||||
>(function Divider({ className, ...otherProps }: DividerProps, ref) {
|
||||
return (
|
||||
<FeatherChevronRight
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-small-regular-0 font-body-small-regular-0 text-icon-neutral-tertiary",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export interface BreadcrumbsRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const BreadcrumbsRoot = React.forwardRef<HTMLDivElement, BreadcrumbsRootProps>(
|
||||
function BreadcrumbsRoot(
|
||||
{ children, className, ...otherProps }: BreadcrumbsRootProps,
|
||||
ref
|
||||
) {
|
||||
return children ? (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex items-center gap-2",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
) : null;
|
||||
}
|
||||
);
|
||||
|
||||
export const Breadcrumbs = Object.assign(BreadcrumbsRoot, {
|
||||
Item,
|
||||
Divider,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { Breadcrumbs as BreadcrumbsComponent } from "./Breadcrumbs";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const Breadcrumbs = BreadcrumbsComponent;
|
||||
@@ -0,0 +1,152 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Button — https://app.subframe.com/f023064a5ca0/library?component=Button_3b777358-b86b-40af-9327-891efc6826fe
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface ButtonRootProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
disabled?: boolean;
|
||||
loading?: boolean;
|
||||
variant?:
|
||||
| "primary"
|
||||
| "secondary"
|
||||
| "tertiary"
|
||||
| "ghost"
|
||||
| "destructive"
|
||||
| "neutral"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "information";
|
||||
size?: "large" | "medium" | "small";
|
||||
children?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
iconRight?: React.ReactNode;
|
||||
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const ButtonRoot = React.forwardRef<HTMLButtonElement, ButtonRootProps>(
|
||||
function ButtonRoot(
|
||||
{
|
||||
disabled = false,
|
||||
loading = false,
|
||||
variant = "primary",
|
||||
size = "medium",
|
||||
children,
|
||||
icon = null,
|
||||
iconRight = null,
|
||||
className,
|
||||
type = "button",
|
||||
...otherProps
|
||||
}: ButtonRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<button
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/3b777358 flex h-9 cursor-pointer items-center justify-center gap-3 rounded-md border-none bg-brand-brand-surface-solid-default px-4 py-2.5 text-left hover:bg-brand-brand-surface-solid-hover active:bg-brand-brand-surface-solid-active focus-within:shadow-focus disabled:cursor-default disabled:bg-surface-neutral-disabled disabled:pointer-events-none hover:disabled:cursor-default hover:disabled:bg-surface-neutral-disabled active:disabled:cursor-default active:disabled:bg-surface-neutral-disabled",
|
||||
{
|
||||
"h-8 gap-2 px-3 py-2": size === "small",
|
||||
"h-12 gap-2.5 px-5 py-3.5": size === "large",
|
||||
"bg-surface-feedback-solid-information hover:bg-surface-feedback-solid-information-hover active:bg-surface-feedback-solid-information-active":
|
||||
variant === "information",
|
||||
"bg-surface-feedback-solid-warning hover:bg-surface-feedback-solid-warning-hover active:bg-surface-feedback-solid-warning-active":
|
||||
variant === "warning",
|
||||
"bg-surface-feedback-solid-positive-default hover:bg-surface-feedback-solid-positive-hover active:bg-surface-feedback-solid-positive-active":
|
||||
variant === "success",
|
||||
"bg-surface-feedback-solid-neutral hover:bg-surface-feedback-solid-neutral-hover active:bg-surface-feedback-solid-neutral-active":
|
||||
variant === "neutral",
|
||||
"bg-surface-feedback-solid-negative-default hover:bg-surface-feedback-solid-negative-hover active:bg-surface-feedback-solid-negative-active":
|
||||
variant === "destructive",
|
||||
"bg-transparent hover:border hover:border-solid hover:border-border-strong hover:bg-surface-neutral-secondary active:bg-surface-neutral-tertiary focus-within:border focus-within:border-solid focus-within:border-border-default focus-within:bg-surface-neutral-secondary":
|
||||
variant === "ghost",
|
||||
"border border-solid border-border-default bg-surface-neutral-primary hover:border hover:border-solid hover:border-border-strong hover:bg-surface-neutral-secondary active:bg-surface-neutral-tertiary":
|
||||
variant === "tertiary",
|
||||
"bg-brand-brand-secondary-surface-subtle-default hover:bg-brand-brand-secondary-surface-subtle-hover active:bg-brand-brand-secondary-surface-subtle-active":
|
||||
variant === "secondary",
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
type={type}
|
||||
disabled={disabled}
|
||||
{...otherProps}
|
||||
>
|
||||
{icon ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-default-regular-0 font-body-default-regular-0 text-icon-neutral-on-solid group-disabled/3b777358:text-icon-neutral-disabled",
|
||||
{
|
||||
"text-body-large-0 font-body-large-0": size === "large",
|
||||
"text-icon-neutral-secondary": variant === "ghost",
|
||||
"text-icon-neutral-on-subtle": variant === "tertiary",
|
||||
"text-icon-neutral-primary": variant === "secondary",
|
||||
hidden: loading,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden h-4 w-4 flex-none items-center justify-center gap-2",
|
||||
{ "h-5 w-5": size === "large", flex: loading }
|
||||
)}
|
||||
>
|
||||
<SubframeCore.Loader
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-extra-small-regular-150 font-body-extra-small-regular-150 text-icon-neutral-on-solid group-disabled/3b777358:text-icon-neutral-disabled group-hover/3b777358:group-disabled/3b777358:text-icon-neutral-on-solid group-active/3b777358:group-disabled/3b777358:text-icon-neutral-disabled",
|
||||
{
|
||||
"text-icon-neutral-secondary": variant === "ghost",
|
||||
"text-icon-neutral-on-subtle": variant === "tertiary",
|
||||
"text-icon-neutral-primary": variant === "secondary",
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{children ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"whitespace-nowrap text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-on-solid group-disabled/3b777358:text-text-neutral-disabled",
|
||||
{
|
||||
"text-body-large-0 font-body-large-0": size === "large",
|
||||
"text-text-neutral-primary":
|
||||
variant === "ghost" ||
|
||||
variant === "tertiary" ||
|
||||
variant === "secondary",
|
||||
"hidden text-body-small-medium-150 font-body-small-medium-150":
|
||||
loading,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
{iconRight ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-default-regular-150 font-body-default-regular-150 text-icon-neutral-on-solid group-disabled/3b777358:text-icon-neutral-disabled",
|
||||
{
|
||||
"text-body-large font-body-large": size === "large",
|
||||
"text-icon-neutral-secondary": variant === "ghost",
|
||||
"text-icon-neutral-on-subtle": variant === "tertiary",
|
||||
"text-icon-neutral-primary": variant === "secondary",
|
||||
hidden: loading,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{iconRight}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const Button = ButtonRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { Button as ButtonComponent } from "./Button";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const Button = ButtonComponent;
|
||||
@@ -0,0 +1,51 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Button Group — https://app.subframe.com/f023064a5ca0/library?component=Button+Group_e0379327-50f0-4852-9ae1-ad09b4bf01d3
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface ButtonGroupRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
vertical?: boolean;
|
||||
align?: "start" | "center" | "end" | "stretch";
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const ButtonGroupRoot = React.forwardRef<HTMLDivElement, ButtonGroupRootProps>(
|
||||
function ButtonGroupRoot(
|
||||
{
|
||||
vertical = false,
|
||||
align = "start",
|
||||
children,
|
||||
className,
|
||||
...otherProps
|
||||
}: ButtonGroupRootProps,
|
||||
ref
|
||||
) {
|
||||
return children ? (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/e0379327 flex w-full items-center gap-2",
|
||||
{
|
||||
"justify-start items-stretch [&>*]:grow [&>*]:basis-0":
|
||||
align === "stretch",
|
||||
"justify-end": align === "end",
|
||||
"justify-center": align === "center",
|
||||
"flex-col items-start": vertical,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
) : null;
|
||||
}
|
||||
);
|
||||
|
||||
export const ButtonGroup = ButtonGroupRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { ButtonGroup as ButtonGroupComponent } from "./ButtonGroup";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const ButtonGroup = ButtonGroupComponent;
|
||||
@@ -0,0 +1,53 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Calendar — https://app.subframe.com/f023064a5ca0/library?component=Calendar_5a87e517-ace2-49af-adcf-076c97ec3921
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export type CalendarRootProps = React.ComponentProps<
|
||||
typeof SubframeCore.Calendar
|
||||
> & { className?: string };
|
||||
|
||||
const CalendarRoot = React.forwardRef<
|
||||
React.ElementRef<typeof SubframeCore.Calendar>,
|
||||
CalendarRootProps
|
||||
>(function CalendarRoot({ className, ...otherProps }: CalendarRootProps, ref) {
|
||||
return (
|
||||
<SubframeCore.Calendar
|
||||
className={className}
|
||||
ref={ref}
|
||||
classNames={{
|
||||
root: "relative border-box",
|
||||
month: "flex flex-col gap-4",
|
||||
months: "relative flex flex-wrap max-w-fit gap-4",
|
||||
nav: "absolute flex items-center justify-between h-8 w-full p-0.5",
|
||||
month_caption: "flex items-center justify-center h-8",
|
||||
caption_label: "text-body-bold font-body-bold text-default-font",
|
||||
button_previous:
|
||||
"inline-flex items-center justify-center h-8 w-8 bg-transparent rounded-md border-none bg-transparent hover:bg-neutral-50 active:bg-neutral-100 border-none",
|
||||
button_next:
|
||||
"inline-flex items-center justify-center h-8 w-8 bg-transparent rounded-md border-none bg-transparent hover:bg-neutral-50 active:bg-neutral-100 border-none",
|
||||
chevron: "text-[18px] font-[500] leading-[18px] text-neutral-600",
|
||||
weeks: "flex flex-col gap-2",
|
||||
weekdays: "flex pb-4",
|
||||
weekday: "w-8 text-caption-bold font-caption-bold text-subtext-color",
|
||||
week: "flex rounded-lg overflow-hidden",
|
||||
day: "group flex p-0 cursor-pointer items-center justify-center text-body font-body text-default-font h-8 w-8",
|
||||
day_button:
|
||||
"flex h-8 w-8 cursor-pointer items-center justify-center gap-2 rounded-lg border-none hover:bg-neutral-100 group-[.selected]:bg-brand-600 group-[.selected]:text-white group-[.outside]:bg-transparent group-[.outside.selected]:bg-neutral-100 group-[.outside.range-start]:bg-neutral-100 group-[.outside.range-end]:bg-neutral-100 group-[.outside.range-start]:rounded-l-lg group-[.outside.range-start]:rounded-r-none group-[.outside.range-end]:rounded-l-none group-[.outside.range-end]:rounded-r-lg group-[.outside]:!text-neutral-400 group-[.outside]:hover:bg-neutral-100 group-[.range-middle.selected]:bg-neutral-100 group-[.range-middle.selected]:text-default-font group-[.range-middle.selected]:hover:bg-neutral-100 group-[.range-middle.selected]:rounded-none",
|
||||
selected: "selected",
|
||||
outside: "outside",
|
||||
range_start: "range-start bg-neutral-100 rounded-l-lg",
|
||||
range_middle: "range-middle",
|
||||
range_end: "range-end bg-neutral-100 rounded-r-lg",
|
||||
}}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const Calendar = CalendarRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { Calendar as CalendarComponent } from "./Calendar";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const Calendar = CalendarComponent;
|
||||
@@ -0,0 +1,65 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Card — https://app.subframe.com/f023064a5ca0/library?component=Card_56067c50-02ce-4c7c-8866-9db30e23ecc0
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface CardRootProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
size?: "extra-small" | "small" | "medium" | "large" | "extra-large";
|
||||
variant?:
|
||||
| "default"
|
||||
| "neutral"
|
||||
| "information"
|
||||
| "positive"
|
||||
| "warning"
|
||||
| "negative";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const CardRoot = React.forwardRef<HTMLDivElement, CardRootProps>(
|
||||
function CardRoot(
|
||||
{
|
||||
children,
|
||||
size = "medium",
|
||||
variant = "default",
|
||||
className,
|
||||
...otherProps
|
||||
}: CardRootProps,
|
||||
ref
|
||||
) {
|
||||
return children ? (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/56067c50 flex min-w-[384px] max-w-[448px] flex-col items-start gap-4 rounded-md border border-solid border-border-default bg-surface-neutral-secondary px-4 py-4 shadow-sm",
|
||||
{
|
||||
"border border-solid border-border-negative-subtle bg-surface-feedback-subtle-negative":
|
||||
variant === "negative",
|
||||
"border border-solid border-border-warning-subtle bg-surface-feedback-subtle-warning":
|
||||
variant === "warning",
|
||||
"border border-solid border-border-positive-subtle bg-surface-feedback-subtle-positive":
|
||||
variant === "positive",
|
||||
"border border-solid border-border-information-subtle bg-surface-feedback-subtle-information":
|
||||
variant === "information",
|
||||
"border border-solid border-surface-feedback-subtle-neutral bg-surface-feedback-subtle-neutral":
|
||||
variant === "neutral",
|
||||
"px-8 py-8": size === "extra-large",
|
||||
"px-6 py-6": size === "large",
|
||||
"px-2 py-2": size === "small",
|
||||
"px-1 py-1": size === "extra-small",
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
) : null;
|
||||
}
|
||||
);
|
||||
|
||||
export const Card = CardRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { Card as CardComponent } from "./Card";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const Card = CardComponent;
|
||||
@@ -0,0 +1,84 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Button — https://app.subframe.com/f023064a5ca0/library?component=Button_3b777358-b86b-40af-9327-891efc6826fe
|
||||
* Card Icon — https://app.subframe.com/f023064a5ca0/library?component=Card+Icon_41867f0d-7a6a-4e51-9b08-6cce5a3070b1
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherInfo } from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
import { Button } from "../Button";
|
||||
|
||||
export interface CardIconRootProps
|
||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
||||
outlined?: boolean;
|
||||
layout?: "horizontal" | "vertical";
|
||||
hideIcon?: boolean;
|
||||
title?: React.ReactNode;
|
||||
body?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const CardIconRoot = React.forwardRef<HTMLDivElement, CardIconRootProps>(
|
||||
function CardIconRoot(
|
||||
{
|
||||
outlined = false,
|
||||
layout = "horizontal",
|
||||
hideIcon = false,
|
||||
title,
|
||||
body,
|
||||
className,
|
||||
...otherProps
|
||||
}: CardIconRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/41867f0d flex min-w-[448px] max-w-[448px] items-start gap-6 px-6 py-6",
|
||||
{
|
||||
"flex-col": layout === "vertical",
|
||||
"rounded-md border border-solid border-border-default bg-surface-neutral-primary":
|
||||
outlined,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames("flex items-start gap-4", {
|
||||
hidden: hideIcon,
|
||||
})}
|
||||
>
|
||||
<FeatherInfo className="font-['Inter'] text-[24px] font-[400] leading-[24px] text-icon-neutral-primary" />
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-6">
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full flex-col items-start gap-3 pr-6",
|
||||
{ "gap-6": layout === "vertical" }
|
||||
)}
|
||||
>
|
||||
{title ? (
|
||||
<span className="w-full text-body-large-0 font-body-large-0 text-text-neutral-primary">
|
||||
{title}
|
||||
</span>
|
||||
) : null}
|
||||
<div className="flex w-full flex-col items-start gap-5">
|
||||
{body ? (
|
||||
<span className="w-full break-words text-body-default-regular-150 font-body-default-regular-150 text-text-neutral-secondary">
|
||||
{body}
|
||||
</span>
|
||||
) : null}
|
||||
<Button>Button</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const CardIcon = CardIconRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { CardIcon as CardIconComponent } from "./CardIcon";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const CardIcon = CardIconComponent;
|
||||
@@ -0,0 +1,83 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Button — https://app.subframe.com/f023064a5ca0/library?component=Button_3b777358-b86b-40af-9327-891efc6826fe
|
||||
* Card Image — https://app.subframe.com/f023064a5ca0/library?component=Card+Image_1f735087-2381-42e1-8ceb-491a8bc63948
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
import { Button } from "../Button";
|
||||
|
||||
export interface CardImageRootProps
|
||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
||||
outlined?: boolean;
|
||||
layout?: "horizontal" | "vertical";
|
||||
hideImage?: boolean;
|
||||
title?: React.ReactNode;
|
||||
body?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const CardImageRoot = React.forwardRef<HTMLDivElement, CardImageRootProps>(
|
||||
function CardImageRoot(
|
||||
{
|
||||
outlined = false,
|
||||
layout = "horizontal",
|
||||
hideImage = false,
|
||||
title,
|
||||
body,
|
||||
className,
|
||||
...otherProps
|
||||
}: CardImageRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/1f735087 flex min-w-[448px] max-w-[448px] items-start gap-6 px-6 py-6",
|
||||
{
|
||||
"flex-col": layout === "vertical",
|
||||
"rounded-md border border-solid border-border-default bg-surface-neutral-primary":
|
||||
outlined,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<img
|
||||
className={SubframeUtils.twClassNames(
|
||||
"h-40 w-40 flex-none object-cover",
|
||||
{ hidden: hideImage }
|
||||
)}
|
||||
src="https://res.cloudinary.com/subframe/image/upload/v1711417507/shared/y2rsnhq3mex4auk54aye.png"
|
||||
/>
|
||||
<div className="flex flex-col items-start gap-6">
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full flex-col items-start gap-3 pr-6",
|
||||
{ "gap-6": layout === "vertical" }
|
||||
)}
|
||||
>
|
||||
{title ? (
|
||||
<span className="w-full text-body-large-0 font-body-large-0 text-icon-neutral-primary">
|
||||
{title}
|
||||
</span>
|
||||
) : null}
|
||||
<div className="flex w-full flex-col items-start gap-5">
|
||||
{body ? (
|
||||
<span className="w-full break-words text-body-default-regular-150 font-body-default-regular-150 text-icon-neutral-primary">
|
||||
{body}
|
||||
</span>
|
||||
) : null}
|
||||
<Button>Button</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const CardImage = CardImageRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { CardImage as CardImageComponent } from "./CardImage";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const CardImage = CardImageComponent;
|
||||
@@ -0,0 +1,145 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Checkbox — https://app.subframe.com/f023064a5ca0/library?component=Checkbox_3816e3b5-c48c-499b-b45e-0777c6972523
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherCheck } from "@subframe/core";
|
||||
import { FeatherMinus } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface CheckboxRootProps
|
||||
extends React.ComponentProps<typeof SubframeCore.Checkbox.Root> {
|
||||
label?: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
checked?: boolean;
|
||||
error?: boolean;
|
||||
description?: React.ReactNode;
|
||||
helpText?: React.ReactNode;
|
||||
indeterminate?: boolean;
|
||||
hideLabel?: boolean;
|
||||
onCheckedChange?: (checked: boolean) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const CheckboxRoot = React.forwardRef<HTMLButtonElement, CheckboxRootProps>(
|
||||
function CheckboxRoot(
|
||||
{
|
||||
label,
|
||||
disabled = false,
|
||||
checked = false,
|
||||
error = false,
|
||||
description,
|
||||
helpText,
|
||||
indeterminate = false,
|
||||
hideLabel = false,
|
||||
className,
|
||||
...otherProps
|
||||
}: CheckboxRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<SubframeCore.Checkbox.Root
|
||||
checked={checked}
|
||||
disabled={disabled}
|
||||
asChild={true}
|
||||
{...otherProps}
|
||||
>
|
||||
<button
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/3816e3b5 flex cursor-pointer items-start justify-between text-left",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames("flex items-center pt-0.5", {
|
||||
"px-0 py-0": hideLabel,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex h-4 w-4 flex-none flex-col items-center justify-center gap-2 rounded-sm border-2 border-solid border-border-strong bg-surface-neutral-secondary group-hover/3816e3b5:border-2 group-hover/3816e3b5:border-solid group-hover/3816e3b5:border-brand-brand-border-solid-hover group-focus-within/3816e3b5:shadow-focus group-aria-[checked=true]/3816e3b5:border-2 group-aria-[checked=true]/3816e3b5:border-solid group-aria-[checked=true]/3816e3b5:border-brand-brand-border-solid-default group-aria-[checked=true]/3816e3b5:bg-brand-brand-surface-solid-default group-hover/3816e3b5:group-aria-[checked=true]/3816e3b5:bg-brand-brand-surface-solid-hover group-disabled/3816e3b5:border group-disabled/3816e3b5:border-solid group-disabled/3816e3b5:border-border-disabled group-disabled/3816e3b5:bg-surface-neutral-disabled",
|
||||
{
|
||||
"border-2 border-solid border-border-negative-solid group-hover/3816e3b5:border-2 group-hover/3816e3b5:border-solid group-hover/3816e3b5:border-border-negative-solid":
|
||||
error,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{indeterminate ? (
|
||||
<FeatherMinus
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden text-body-extra-small-semibold-150 font-body-extra-small-semibold-150 text-icon-neutral-on-solid group-aria-[checked=true]/3816e3b5:inline-flex group-disabled/3816e3b5:text-icon-neutral-disabled",
|
||||
{ "inline-flex": indeterminate }
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<FeatherCheck
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden text-body-extra-small-semibold-150 font-body-extra-small-semibold-150 text-icon-neutral-on-solid group-aria-[checked=true]/3816e3b5:inline-flex group-disabled/3816e3b5:text-icon-neutral-disabled",
|
||||
{ "inline-flex": indeterminate }
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden h-4 w-4 flex-none flex-col items-center justify-center gap-2 rounded-round border-2 border-solid border-border-strong bg-surface-neutral-secondary group-hover/3816e3b5:border-brand-brand-border-solid-hover group-focus-within/3816e3b5:shadow-focus group-aria-[checked=true]/3816e3b5:border-2 group-aria-[checked=true]/3816e3b5:border-solid group-aria-[checked=true]/3816e3b5:border-brand-brand-border-solid-default group-disabled/3816e3b5:border-2 group-disabled/3816e3b5:border-solid group-disabled/3816e3b5:border-border-disabled group-disabled/3816e3b5:bg-surface-neutral-disabled",
|
||||
{
|
||||
"border-2 border-solid border-border-negative-solid group-hover/3816e3b5:border-border-negative-solid":
|
||||
error,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden h-2 w-2 flex-none items-start rounded-round bg-brand-brand-surface-solid-default group-hover/3816e3b5:bg-brand-brand-surface-solid-hover group-aria-[checked=true]/3816e3b5:flex group-disabled/3816e3b5:bg-icon-neutral-disabled",
|
||||
{
|
||||
"bg-surface-feedback-solid-negative-default group-hover/3816e3b5:bg-surface-feedback-solid-negative-hover":
|
||||
error,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex flex-col items-start pl-2 group-hover/3816e3b5:grow group-hover/3816e3b5:shrink-0 group-hover/3816e3b5:basis-0",
|
||||
{ hidden: hideLabel }
|
||||
)}
|
||||
>
|
||||
{label ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-primary group-disabled/3816e3b5:text-text-neutral-disabled",
|
||||
{ "text-text-feedback-negative": error }
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
) : null}
|
||||
{description ? (
|
||||
<span className="text-body-extra-small-regular-150 font-body-extra-small-regular-150 text-text-neutral-tertiary group-disabled/3816e3b5:text-text-neutral-disabled">
|
||||
{description}
|
||||
</span>
|
||||
) : null}
|
||||
{helpText ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-extra-small-regular-150 font-body-extra-small-regular-150 text-text-neutral-tertiary",
|
||||
{ "text-text-feedback-negative": error }
|
||||
)}
|
||||
>
|
||||
{helpText}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</button>
|
||||
</SubframeCore.Checkbox.Root>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const Checkbox = CheckboxRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { Checkbox as CheckboxComponent } from "./Checkbox";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const Checkbox = CheckboxComponent;
|
||||
@@ -0,0 +1,68 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Checkbox Card — https://app.subframe.com/f023064a5ca0/library?component=Checkbox+Card_de0b4dfb-3946-4702-be52-5678dd71925a
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherCheck } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface CheckboxCardRootProps
|
||||
extends React.ComponentProps<typeof SubframeCore.Checkbox.Root> {
|
||||
disabled?: boolean;
|
||||
checked?: boolean;
|
||||
showCheckbox?: boolean;
|
||||
children?: React.ReactNode;
|
||||
onCheckedChange?: (checked: boolean) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const CheckboxCardRoot = React.forwardRef<
|
||||
HTMLButtonElement,
|
||||
CheckboxCardRootProps
|
||||
>(function CheckboxCardRoot(
|
||||
{
|
||||
disabled = false,
|
||||
checked = false,
|
||||
showCheckbox = false,
|
||||
children,
|
||||
className,
|
||||
...otherProps
|
||||
}: CheckboxCardRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<SubframeCore.Checkbox.Root
|
||||
checked={checked}
|
||||
disabled={disabled}
|
||||
asChild={true}
|
||||
{...otherProps}
|
||||
>
|
||||
<button
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/de0b4dfb flex cursor-pointer items-center gap-4 rounded-md border border-solid border-border-default bg-surface-neutral-secondary px-2 py-2 text-left hover:bg-surface-neutral-tertiary aria-[checked=true]:border aria-[checked=true]:border-solid aria-[checked=true]:border-brand-brand-secondary-border-subtle-default aria-[checked=true]:bg-surface-ui-subtle-blue-default hover:aria-[checked=true]:bg-surface-ui-subtle-blue-hover disabled:cursor-default disabled:border disabled:border-solid disabled:border-border-disabled disabled:bg-surface-neutral-disabled hover:disabled:cursor-default hover:disabled:bg-surface-neutral-disabled",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden h-4 w-4 flex-none flex-col items-center justify-center rounded-sm border-2 border-solid border-border-strong bg-surface-neutral-secondary group-aria-[checked=true]/de0b4dfb:border-2 group-aria-[checked=true]/de0b4dfb:border-solid group-aria-[checked=true]/de0b4dfb:border-brand-brand-border-solid-default group-aria-[checked=true]/de0b4dfb:bg-brand-brand-surface-solid-default group-disabled/de0b4dfb:border-2 group-disabled/de0b4dfb:border-solid group-disabled/de0b4dfb:border-border-disabled group-disabled/de0b4dfb:bg-surface-neutral-disabled",
|
||||
{ flex: showCheckbox }
|
||||
)}
|
||||
>
|
||||
<FeatherCheck className="hidden text-body-extra-small-semibold-150 font-body-extra-small-semibold-150 text-icon-neutral-on-solid group-aria-[checked=true]/de0b4dfb:inline-flex group-disabled/de0b4dfb:text-icon-neutral-disabled" />
|
||||
</div>
|
||||
{children ? (
|
||||
<div className="flex grow shrink-0 basis-0 items-center gap-4">
|
||||
{children}
|
||||
</div>
|
||||
) : null}
|
||||
</button>
|
||||
</SubframeCore.Checkbox.Root>
|
||||
);
|
||||
});
|
||||
|
||||
export const CheckboxCard = CheckboxCardRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { CheckboxCard as CheckboxCardComponent } from "./CheckboxCard";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const CheckboxCard = CheckboxCardComponent;
|
||||
@@ -0,0 +1,73 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Checkbox Group — https://app.subframe.com/f023064a5ca0/library?component=Checkbox+Group_f9f1b596-c6b3-4d60-aa9a-f34b353f8aa5
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface CheckboxGroupRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
label?: React.ReactNode;
|
||||
helpText?: React.ReactNode;
|
||||
error?: boolean;
|
||||
horizontal?: boolean;
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const CheckboxGroupRoot = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
CheckboxGroupRootProps
|
||||
>(function CheckboxGroupRoot(
|
||||
{
|
||||
label,
|
||||
helpText,
|
||||
error = false,
|
||||
horizontal = false,
|
||||
children,
|
||||
className,
|
||||
...otherProps
|
||||
}: CheckboxGroupRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/f9f1b596 flex flex-col items-start gap-2",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{label ? (
|
||||
<span className="text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-primary">
|
||||
{label}
|
||||
</span>
|
||||
) : null}
|
||||
{children ? (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex flex-col items-start gap-2",
|
||||
{ "flex-row gap-6": horizontal }
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
) : null}
|
||||
{helpText ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-extra-small-regular-150 font-body-extra-small-regular-150 text-text-neutral-tertiary",
|
||||
{ "text-text-feedback-negative": error }
|
||||
)}
|
||||
>
|
||||
{helpText}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const CheckboxGroup = CheckboxGroupRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { CheckboxGroup as CheckboxGroupComponent } from "./CheckboxGroup";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const CheckboxGroup = CheckboxGroupComponent;
|
||||
@@ -0,0 +1,386 @@
|
||||
// @subframe/sync-disable
|
||||
// @ts-nocheck
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Combobox — https://app.subframe.com/f023064a5ca0/library?component=Combobox_8a1be2ee-c1b7-4407-9c21-6448306d2700
|
||||
* Loader — https://app.subframe.com/f023064a5ca0/library?component=Loader_f2e570c8-e463-45c2-aae9-a960146bc5d5
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherCheck } from "@subframe/core";
|
||||
import { FeatherChevronDown } from "@subframe/core";
|
||||
import { FeatherLoader } from "@subframe/core";
|
||||
import { FeatherPlus } from "@subframe/core";
|
||||
import { FeatherSearch } from "@subframe/core";
|
||||
import { FeatherSearchX } from "@subframe/core";
|
||||
import { FeatherX } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
import { Loader } from "../Loader";
|
||||
|
||||
export interface ChipProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Chip = React.forwardRef<HTMLDivElement, ChipProps>(function Chip(
|
||||
{ children, className, ...otherProps }: ChipProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex items-center gap-1 rounded-sm border border-solid border-border-default bg-surface-feedback-subtle-neutral pl-2 pr-1 py-0.5",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{children ? (
|
||||
<span className="whitespace-nowrap text-body-small-medium-0 font-body-small-medium-0 text-text-neutral-on-subtle">
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
<div className="flex h-4 w-4 flex-none flex-col items-center justify-center rounded-sm cursor-pointer hover:bg-surface-feedback-subtle-neutral-hover active:bg-surface-feedback-subtle-neutral-active">
|
||||
<FeatherX className="text-body-small-medium-0 font-body-small-medium-0 text-icon-neutral-tertiary" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export interface ItemProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
selected?: boolean;
|
||||
showCheckbox?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Item = React.forwardRef<HTMLDivElement, ItemProps>(function Item(
|
||||
{
|
||||
children,
|
||||
selected = false,
|
||||
showCheckbox = false,
|
||||
className,
|
||||
...otherProps
|
||||
}: ItemProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/7b9d6745 flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-2 hover:bg-surface-neutral-tertiary active:bg-surface-neutral-secondary",
|
||||
{
|
||||
"bg-surface-ui-subtle-blue-default hover:bg-surface-ui-subtle-blue-hover active:bg-surface-ui-subtle-blue-active":
|
||||
selected,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden h-4 w-4 flex-none flex-col items-center justify-center rounded-sm border-2 border-solid border-border-strong bg-surface-neutral-secondary",
|
||||
{
|
||||
flex: showCheckbox,
|
||||
"border-2 border-solid border-brand-brand-border-solid-default bg-brand-brand-surface-solid-default":
|
||||
selected,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<FeatherCheck
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden text-body-extra-small-semibold-0 font-body-extra-small-semibold-0 text-icon-neutral-on-solid",
|
||||
{ "inline-flex": selected }
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{children ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"grow shrink-0 basis-0 text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-tertiary",
|
||||
{ "text-text-neutral-primary": selected }
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
<FeatherCheck
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden text-body-default-regular-0 font-body-default-regular-0 text-icon-neutral-primary",
|
||||
{ "inline-flex": selected }
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export interface AddItemProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const AddItem = React.forwardRef<HTMLDivElement, AddItemProps>(function AddItem(
|
||||
{ children, className, ...otherProps }: AddItemProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/4458d273 flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-2 group/add-item hover:bg-surface-neutral-tertiary active:bg-surface-neutral-secondary",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<FeatherPlus className="text-body-default-regular-0 font-body-default-regular-0 text-icon-feedback-information" />
|
||||
{children ? (
|
||||
<span className="grow shrink-0 basis-0 text-body-default-medium-0 font-body-default-medium-0 text-text-feedback-information">
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export interface ComboboxRootProps
|
||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, "placeholder"> {
|
||||
disabled?: boolean;
|
||||
error?: boolean;
|
||||
state?: "default" | "typing" | "no-results" | "loading" | "create-new";
|
||||
hasValue?: boolean;
|
||||
open?: boolean;
|
||||
showLabel?: boolean;
|
||||
label?: React.ReactNode;
|
||||
helpText?: React.ReactNode;
|
||||
searchValue?: React.ReactNode;
|
||||
placeholder?: React.ReactNode;
|
||||
createLabel?: React.ReactNode;
|
||||
emptyText?: React.ReactNode;
|
||||
loadingText?: React.ReactNode;
|
||||
message?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
chipChildren?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
size?: "small" | "medium" | "large";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const ComboboxRoot = React.forwardRef<HTMLDivElement, ComboboxRootProps>(
|
||||
function ComboboxRoot(
|
||||
{
|
||||
disabled = false,
|
||||
error = false,
|
||||
state = "default",
|
||||
hasValue = false,
|
||||
open = false,
|
||||
showLabel = false,
|
||||
label,
|
||||
helpText,
|
||||
searchValue,
|
||||
placeholder,
|
||||
createLabel,
|
||||
emptyText,
|
||||
loadingText,
|
||||
message,
|
||||
icon = <FeatherSearch />,
|
||||
chipChildren,
|
||||
children,
|
||||
size = "medium",
|
||||
className,
|
||||
...otherProps
|
||||
}: ComboboxRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/8a1be2ee flex min-w-[192px] max-w-[576px] cursor-pointer flex-col items-start gap-3",
|
||||
{ "gap-0": showLabel },
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden w-full flex-col items-start",
|
||||
{ flex: showLabel }
|
||||
)}
|
||||
>
|
||||
{label ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-default-medium-0 font-body-default-medium-0 text-text-neutral-primary",
|
||||
{ "text-text-feedback-negative": error }
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
) : null}
|
||||
{helpText ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-small-regular-0 font-body-small-regular-0 text-text-neutral-tertiary mt-2",
|
||||
{ "text-text-feedback-negative": error }
|
||||
)}
|
||||
>
|
||||
{helpText}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full grow shrink-0 basis-0 flex-col items-start gap-1 rounded-sm relative",
|
||||
{ "mt-2": showLabel }
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex h-9 w-full flex-none flex-col items-start rounded-sm border border-solid border-border-strong bg-surface-neutral-primary group-hover/8a1be2ee:border group-hover/8a1be2ee:border-solid group-hover/8a1be2ee:border-brand-brand-border-solid-hover group-hover/8a1be2ee:bg-surface-neutral-secondary group-focus-within/8a1be2ee:bg-surface-neutral-secondary group-focus-within/8a1be2ee:shadow-focus",
|
||||
{
|
||||
"h-12": size === "large",
|
||||
"h-8": size === "small",
|
||||
"border border-solid border-border-negative-solid group-hover/8a1be2ee:border group-hover/8a1be2ee:border-solid group-hover/8a1be2ee:border-border-negative-solid group-focus-within/8a1be2ee:border-2 group-focus-within/8a1be2ee:border-solid group-focus-within/8a1be2ee:border-border-negative-solid group-focus-within/8a1be2ee:shadow-none":
|
||||
error,
|
||||
"border border-solid border-border-disabled bg-surface-neutral-disabled group-hover/8a1be2ee:border group-hover/8a1be2ee:border-solid group-hover/8a1be2ee:border-border-disabled group-hover/8a1be2ee:bg-surface-neutral-disabled group-focus-within/8a1be2ee:bg-surface-neutral-disabled group-focus-within/8a1be2ee:shadow-none":
|
||||
disabled,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div className="flex w-full grow shrink-0 basis-0 flex-wrap items-center gap-2 px-3">
|
||||
{icon ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-default-regular-0 font-body-default-regular-0 text-icon-neutral-subtle",
|
||||
{ "text-icon-feedback-negative": error }
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
{chipChildren ? (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden flex-wrap items-center gap-1.5",
|
||||
{ flex: hasValue }
|
||||
)}
|
||||
>
|
||||
{chipChildren}
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex grow shrink-0 basis-0 items-center gap-1",
|
||||
{ hidden: hasValue }
|
||||
)}
|
||||
>
|
||||
<input
|
||||
className={SubframeUtils.twClassNames(
|
||||
"h-6 min-w-[64px] grow shrink-0 basis-0 text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-secondary outline-none placeholder:text-text-neutral-subtle",
|
||||
{ hidden: hasValue, "text-text-neutral-disabled": disabled }
|
||||
)}
|
||||
placeholder={placeholder}
|
||||
value=""
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
<FeatherChevronDown
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-default-medium-0 font-body-default-medium-0 text-icon-neutral-tertiary",
|
||||
{
|
||||
"text-icon-feedback-negative": error,
|
||||
"hidden text-icon-neutral-disabled": disabled,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
<Loader
|
||||
className={SubframeUtils.twClassNames("hidden", {
|
||||
flex: state === "loading",
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{message ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden text-body-extra-small-regular-0 font-body-extra-small-regular-0 text-text-feedback-negative mt-1",
|
||||
{ inline: error }
|
||||
)}
|
||||
>
|
||||
{message}
|
||||
</span>
|
||||
) : null}
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden w-full flex-col items-start gap-0.5 overflow-hidden rounded-sm border border-solid border-border-default bg-surface-neutral-primary px-1 py-1 shadow-md absolute left-0 right-0 top-full z-50 mt-1",
|
||||
{ flex: open }
|
||||
)}
|
||||
>
|
||||
{children ? (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full flex-col items-start gap-0.5",
|
||||
{
|
||||
hidden:
|
||||
state === "create-new" ||
|
||||
state === "loading" ||
|
||||
state === "no-results",
|
||||
}
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden w-full items-center gap-2 px-2 py-3",
|
||||
{ flex: state === "no-results" }
|
||||
)}
|
||||
>
|
||||
<FeatherSearchX className="text-body-default-regular-0 font-body-default-regular-0 text-icon-neutral-tertiary" />
|
||||
{emptyText ? (
|
||||
<span className="text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-tertiary">
|
||||
{emptyText}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden w-full items-center gap-2 px-2 py-3",
|
||||
{ flex: state === "loading" }
|
||||
)}
|
||||
>
|
||||
<FeatherLoader className="text-body-default-regular-0 font-body-default-regular-0 text-icon-neutral-tertiary animate-spin" />
|
||||
{loadingText ? (
|
||||
<span className="text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-tertiary">
|
||||
{loadingText}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden w-full items-center gap-2 rounded-sm px-2 py-2 cursor-pointer hover:bg-surface-neutral-tertiary active:bg-surface-neutral-secondary",
|
||||
{ flex: state === "create-new" }
|
||||
)}
|
||||
>
|
||||
<FeatherPlus className="text-body-default-regular-0 font-body-default-regular-0 text-icon-feedback-information" />
|
||||
{createLabel ? (
|
||||
<span className="grow shrink-0 basis-0 text-body-default-medium-0 font-body-default-medium-0 text-text-feedback-information">
|
||||
{createLabel}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const Combobox = Object.assign(ComboboxRoot, {
|
||||
Chip,
|
||||
Item,
|
||||
AddItem,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { Combobox as ComboboxComponent } from "./Combobox";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const Combobox = ComboboxComponent;
|
||||
@@ -0,0 +1,117 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Context Menu — https://app.subframe.com/f023064a5ca0/library?component=Context+Menu_f8a49f07-fa5b-46c8-9399-cbbf0930cc62
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherStar } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface ContextDividerProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const ContextDivider = React.forwardRef<HTMLDivElement, ContextDividerProps>(
|
||||
function ContextDivider(
|
||||
{ className, ...otherProps }: ContextDividerProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full items-start gap-2 px-1 py-1",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className="flex h-px grow shrink-0 basis-0 flex-col items-center gap-2 bg-border-default" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export interface ContextItemProps
|
||||
extends React.ComponentProps<typeof SubframeCore.ContextMenu.Item> {
|
||||
children?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
rightSlot?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const ContextItem = React.forwardRef<HTMLDivElement, ContextItemProps>(
|
||||
function ContextItem(
|
||||
{
|
||||
children,
|
||||
icon = <FeatherStar />,
|
||||
rightSlot,
|
||||
className,
|
||||
...otherProps
|
||||
}: ContextItemProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<SubframeCore.ContextMenu.Item asChild={true} {...otherProps}>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/9358ee78 flex h-8 w-full cursor-pointer items-center gap-2 rounded-md px-2 hover:bg-surface-neutral-tertiary active:bg-surface-neutral-primary data-[highlighted]:bg-surface-neutral-tertiary",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
<div className="flex h-4 w-4 flex-none items-center justify-center gap-2">
|
||||
{icon ? (
|
||||
<SubframeCore.IconWrapper className="text-body-default-regular-0 font-body-default-regular-0 text-icon-neutral-primary">
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
</div>
|
||||
{children ? (
|
||||
<span className="line-clamp-1 grow shrink-0 basis-0 text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-primary">
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
{rightSlot ? (
|
||||
<div className="flex flex-col items-end justify-center gap-2">
|
||||
{rightSlot}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</SubframeCore.ContextMenu.Item>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export interface ContextMenuRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const ContextMenuRoot = React.forwardRef<HTMLDivElement, ContextMenuRootProps>(
|
||||
function ContextMenuRoot(
|
||||
{ children, className, ...otherProps }: ContextMenuRootProps,
|
||||
ref
|
||||
) {
|
||||
return children ? (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex min-w-[192px] flex-col items-start rounded-md border border-solid border-border-default bg-surface-neutral-secondary px-1 py-1 shadow-lg",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
) : null;
|
||||
}
|
||||
);
|
||||
|
||||
export const ContextMenu = Object.assign(ContextMenuRoot, {
|
||||
ContextDivider,
|
||||
ContextItem,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { ContextMenu as ContextMenuComponent } from "./ContextMenu";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const ContextMenu = ContextMenuComponent;
|
||||
@@ -0,0 +1,77 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Copy to clipboard button — https://app.subframe.com/f023064a5ca0/library?component=Copy+to+clipboard+button_e8c76626-6462-4f2f-b595-38d530d427e8
|
||||
* Tooltip — https://app.subframe.com/f023064a5ca0/library?component=Tooltip_ccebd1e9-f6ac-4737-8376-0dfacd90c9f3
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherClipboard } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
import { Tooltip } from "../Tooltip";
|
||||
|
||||
export interface CopyToClipboardButtonRootProps
|
||||
extends Omit<
|
||||
React.ComponentProps<typeof SubframeCore.CopyToClipboard.Root>,
|
||||
"clipboardText"
|
||||
> {
|
||||
clipboardText?: React.ReactNode;
|
||||
tooltipText?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
onCopy?: () => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const CopyToClipboardButtonRoot = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
CopyToClipboardButtonRootProps
|
||||
>(function CopyToClipboardButtonRoot(
|
||||
{
|
||||
clipboardText,
|
||||
tooltipText,
|
||||
icon = <FeatherClipboard />,
|
||||
className,
|
||||
...otherProps
|
||||
}: CopyToClipboardButtonRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<SubframeCore.Tooltip.Provider>
|
||||
<SubframeCore.Tooltip.Root>
|
||||
<SubframeCore.Tooltip.Trigger asChild={true}>
|
||||
<SubframeCore.CopyToClipboard.Root
|
||||
clipboardText={clipboardText as string}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/e8c76626 flex h-6 w-6 cursor-pointer flex-col items-center justify-center gap-2 rounded-md hover:bg-surface-neutral-tertiary",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
{icon ? (
|
||||
<SubframeCore.IconWrapper className="text-body-default-regular-0 font-body-default-regular-0 text-icon-neutral-secondary group-hover/e8c76626:text-icon-neutral-primary">
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
</div>
|
||||
</SubframeCore.CopyToClipboard.Root>
|
||||
</SubframeCore.Tooltip.Trigger>
|
||||
<SubframeCore.Tooltip.Portal>
|
||||
<SubframeCore.Tooltip.Content
|
||||
side="bottom"
|
||||
align="center"
|
||||
sideOffset={8}
|
||||
asChild={true}
|
||||
>
|
||||
<Tooltip>{tooltipText}</Tooltip>
|
||||
</SubframeCore.Tooltip.Content>
|
||||
</SubframeCore.Tooltip.Portal>
|
||||
</SubframeCore.Tooltip.Root>
|
||||
</SubframeCore.Tooltip.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
export const CopyToClipboardButton = CopyToClipboardButtonRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { CopyToClipboardButton as CopyToClipboardButtonComponent } from "./CopyToClipboardButton";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const CopyToClipboardButton = CopyToClipboardButtonComponent;
|
||||
@@ -0,0 +1,679 @@
|
||||
// @subframe/sync-disable
|
||||
// @ts-nocheck
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Date Picker — https://app.subframe.com/f023064a5ca0/library?component=Date+Picker_e0461776-caeb-4bb3-a9a1-7df3bcfe9afe
|
||||
* Dropdown Menu select — https://app.subframe.com/f023064a5ca0/library?component=Dropdown+Menu+select_99951515-459b-4286-919e-a89e7549b43b
|
||||
* Icon Button — https://app.subframe.com/f023064a5ca0/library?component=Icon+Button_af9405b1-8c54-4e01-9786-5aad308224f6
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherCalendar } from "@subframe/core";
|
||||
import { FeatherChevronDown } from "@subframe/core";
|
||||
import { FeatherChevronLeft } from "@subframe/core";
|
||||
import { FeatherChevronRight } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
import { DropdownMenuSelect } from "../DropdownMenuSelect";
|
||||
import { IconButton } from "../IconButton";
|
||||
|
||||
export interface WeekdayProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Weekday = React.forwardRef<HTMLDivElement, WeekdayProps>(function Weekday(
|
||||
{ children, className, ...otherProps }: WeekdayProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex h-8 w-9 items-center justify-center",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{children ? (
|
||||
<span className="text-body-extra-small-medium-0 font-body-extra-small-medium-0 text-text-neutral-tertiary">
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export interface SelectProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
grow?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Select = React.forwardRef<HTMLDivElement, SelectProps>(function Select(
|
||||
{ children, grow = false, className, ...otherProps }: SelectProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/f2c7e363 flex cursor-pointer items-center justify-between rounded-sm border border-solid border-border-default bg-surface-neutral-secondary px-2 py-1.5 hover:bg-surface-neutral-tertiary",
|
||||
{ "w-full": grow },
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className="flex items-center gap-2 pr-2">
|
||||
{children ? (
|
||||
<span className="text-body-small-medium-0 font-body-small-medium-0 text-text-neutral-primary">
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<FeatherChevronDown className="text-body-small-medium-0 font-body-small-medium-0 text-icon-neutral-tertiary" />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export interface CalendarPanelProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
month?: React.ReactNode;
|
||||
year?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const CalendarPanel = React.forwardRef<HTMLDivElement, CalendarPanelProps>(
|
||||
function CalendarPanel(
|
||||
{ month, year, className, ...otherProps }: CalendarPanelProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex flex-col items-start gap-2",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className="flex w-80 flex-col items-start overflow-hidden rounded-md border border-solid border-border-default shadow-md">
|
||||
<div className="flex w-full flex-col items-start bg-surface-neutral-tertiary">
|
||||
<div className="flex w-full items-center justify-center px-3 py-3">
|
||||
<div className="flex grow shrink-0 basis-0 items-center justify-center gap-1">
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="extra-small"
|
||||
square={true}
|
||||
icon={<FeatherChevronLeft />}
|
||||
/>
|
||||
<DatePicker.MonthSelect />
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="extra-small"
|
||||
square={true}
|
||||
icon={<FeatherChevronRight />}
|
||||
/>
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="extra-small"
|
||||
square={true}
|
||||
icon={<FeatherChevronLeft />}
|
||||
/>
|
||||
<DatePicker.YearSelect />
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="extra-small"
|
||||
square={true}
|
||||
icon={<FeatherChevronRight />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between px-3">
|
||||
<DatePicker.Weekday>Mo</DatePicker.Weekday>
|
||||
<DatePicker.Weekday>Di</DatePicker.Weekday>
|
||||
<DatePicker.Weekday>Mi</DatePicker.Weekday>
|
||||
<DatePicker.Weekday>Do</DatePicker.Weekday>
|
||||
<DatePicker.Weekday>Fr</DatePicker.Weekday>
|
||||
<DatePicker.Weekday>Sa</DatePicker.Weekday>
|
||||
<DatePicker.Weekday>So</DatePicker.Weekday>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex h-px w-full flex-none flex-col items-center gap-2 bg-[#e5e5e5]" />
|
||||
<div className="flex w-full flex-col items-center gap-1 bg-surface-neutral-secondary px-3 py-3">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<DatePicker.Day outside={true}>27</DatePicker.Day>
|
||||
<DatePicker.Day outside={true}>28</DatePicker.Day>
|
||||
<DatePicker.Day outside={true}>29</DatePicker.Day>
|
||||
<DatePicker.Day outside={true}>30</DatePicker.Day>
|
||||
<DatePicker.Day outside={true}>31</DatePicker.Day>
|
||||
<DatePicker.Day>1</DatePicker.Day>
|
||||
<DatePicker.Day>2</DatePicker.Day>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<DatePicker.Day>3</DatePicker.Day>
|
||||
<DatePicker.Day>4</DatePicker.Day>
|
||||
<DatePicker.Day>5</DatePicker.Day>
|
||||
<DatePicker.Day>6</DatePicker.Day>
|
||||
<DatePicker.Day>7</DatePicker.Day>
|
||||
<DatePicker.Day>8</DatePicker.Day>
|
||||
<DatePicker.Day>9</DatePicker.Day>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<DatePicker.Day>10</DatePicker.Day>
|
||||
<DatePicker.Day>11</DatePicker.Day>
|
||||
<DatePicker.Day>12</DatePicker.Day>
|
||||
<DatePicker.Day>13</DatePicker.Day>
|
||||
<DatePicker.Day selected={true}>14</DatePicker.Day>
|
||||
<DatePicker.Day>15</DatePicker.Day>
|
||||
<DatePicker.Day>16</DatePicker.Day>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<DatePicker.Day>17</DatePicker.Day>
|
||||
<DatePicker.Day>18</DatePicker.Day>
|
||||
<DatePicker.Day today={true}>19</DatePicker.Day>
|
||||
<DatePicker.Day>20</DatePicker.Day>
|
||||
<DatePicker.Day>21</DatePicker.Day>
|
||||
<DatePicker.Day>22</DatePicker.Day>
|
||||
<DatePicker.Day>23</DatePicker.Day>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<DatePicker.Day>24</DatePicker.Day>
|
||||
<DatePicker.Day>25</DatePicker.Day>
|
||||
<DatePicker.Day>26</DatePicker.Day>
|
||||
<DatePicker.Day>27</DatePicker.Day>
|
||||
<DatePicker.Day>28</DatePicker.Day>
|
||||
<DatePicker.Day outside={true}>1</DatePicker.Day>
|
||||
<DatePicker.Day outside={true}>2</DatePicker.Day>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="hidden w-80 flex-col items-start overflow-hidden rounded-md border border-solid border-border-default shadow-md">
|
||||
<div className="flex w-full flex-col items-start bg-surface-neutral-tertiary">
|
||||
<div className="flex w-full items-center justify-center px-3 py-3">
|
||||
<div className="flex grow shrink-0 basis-0 items-center justify-center gap-1">
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="extra-small"
|
||||
square={true}
|
||||
icon={<FeatherChevronLeft />}
|
||||
/>
|
||||
<DatePicker.MonthSelect />
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="extra-small"
|
||||
square={true}
|
||||
icon={<FeatherChevronRight />}
|
||||
/>
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="extra-small"
|
||||
square={true}
|
||||
icon={<FeatherChevronLeft />}
|
||||
/>
|
||||
<DatePicker.YearSelect />
|
||||
<IconButton
|
||||
variant="ghost"
|
||||
size="extra-small"
|
||||
square={true}
|
||||
icon={<FeatherChevronRight />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between px-3">
|
||||
<DatePicker.Weekday>Mo</DatePicker.Weekday>
|
||||
<DatePicker.Weekday>Di</DatePicker.Weekday>
|
||||
<DatePicker.Weekday>Mi</DatePicker.Weekday>
|
||||
<DatePicker.Weekday>Do</DatePicker.Weekday>
|
||||
<DatePicker.Weekday>Fr</DatePicker.Weekday>
|
||||
<DatePicker.Weekday>Sa</DatePicker.Weekday>
|
||||
<DatePicker.Weekday>So</DatePicker.Weekday>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex h-px w-full flex-none flex-col items-center gap-2 bg-[#e5e5e5]" />
|
||||
<div className="flex w-full flex-col items-center gap-1 bg-surface-neutral-secondary px-3 py-3">
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<DatePicker.Day outside={true}>27</DatePicker.Day>
|
||||
<DatePicker.Day outside={true}>28</DatePicker.Day>
|
||||
<DatePicker.Day outside={true}>29</DatePicker.Day>
|
||||
<DatePicker.Day outside={true}>30</DatePicker.Day>
|
||||
<DatePicker.Day outside={true}>31</DatePicker.Day>
|
||||
<DatePicker.Day>1</DatePicker.Day>
|
||||
<DatePicker.Day>2</DatePicker.Day>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<DatePicker.Day>3</DatePicker.Day>
|
||||
<DatePicker.Day>4</DatePicker.Day>
|
||||
<DatePicker.Day>5</DatePicker.Day>
|
||||
<DatePicker.Day>6</DatePicker.Day>
|
||||
<DatePicker.Day>7</DatePicker.Day>
|
||||
<DatePicker.Day>8</DatePicker.Day>
|
||||
<DatePicker.Day>9</DatePicker.Day>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<DatePicker.Day>10</DatePicker.Day>
|
||||
<DatePicker.Day>11</DatePicker.Day>
|
||||
<DatePicker.Day>12</DatePicker.Day>
|
||||
<DatePicker.Day>13</DatePicker.Day>
|
||||
<DatePicker.Day selected={true}>14</DatePicker.Day>
|
||||
<DatePicker.Day>15</DatePicker.Day>
|
||||
<DatePicker.Day>16</DatePicker.Day>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<DatePicker.Day>17</DatePicker.Day>
|
||||
<DatePicker.Day>18</DatePicker.Day>
|
||||
<DatePicker.Day today={true}>19</DatePicker.Day>
|
||||
<DatePicker.Day>20</DatePicker.Day>
|
||||
<DatePicker.Day>21</DatePicker.Day>
|
||||
<DatePicker.Day>22</DatePicker.Day>
|
||||
<DatePicker.Day>23</DatePicker.Day>
|
||||
</div>
|
||||
<div className="flex w-full items-center justify-between">
|
||||
<DatePicker.Day>24</DatePicker.Day>
|
||||
<DatePicker.Day>25</DatePicker.Day>
|
||||
<DatePicker.Day>26</DatePicker.Day>
|
||||
<DatePicker.Day>27</DatePicker.Day>
|
||||
<DatePicker.Day>28</DatePicker.Day>
|
||||
<DatePicker.Day outside={true}>1</DatePicker.Day>
|
||||
<DatePicker.Day outside={true}>2</DatePicker.Day>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export interface MonthSelectProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const MonthSelect = React.forwardRef<HTMLDivElement, MonthSelectProps>(
|
||||
function MonthSelect({ className, ...otherProps }: MonthSelectProps, ref) {
|
||||
return (
|
||||
<SubframeCore.DropdownMenu.Root>
|
||||
<SubframeCore.DropdownMenu.Trigger asChild={true}>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex flex-col items-start gap-2",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<DatePicker.Select>September</DatePicker.Select>
|
||||
</div>
|
||||
</SubframeCore.DropdownMenu.Trigger>
|
||||
<SubframeCore.DropdownMenu.Portal>
|
||||
<SubframeCore.DropdownMenu.Content
|
||||
side="bottom"
|
||||
align="start"
|
||||
sideOffset={4}
|
||||
asChild={true}
|
||||
>
|
||||
<DropdownMenuSelect className="min-w-0">
|
||||
<div className="flex w-full flex-col items-start gap-0.5">
|
||||
<DatePicker.YearCell>Januar</DatePicker.YearCell>
|
||||
<DatePicker.YearCell selected={true}>
|
||||
Februar
|
||||
</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>März</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>April</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>Mai</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>Juni</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>Juli</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>August</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>September</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>Oktober</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>November</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>Dezember</DatePicker.YearCell>
|
||||
</div>
|
||||
</DropdownMenuSelect>
|
||||
</SubframeCore.DropdownMenu.Content>
|
||||
</SubframeCore.DropdownMenu.Portal>
|
||||
</SubframeCore.DropdownMenu.Root>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export interface YearSelectProps
|
||||
extends React.ComponentProps<typeof DatePicker.Select> {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const YearSelect = React.forwardRef<
|
||||
React.ElementRef<typeof DatePicker.Select>,
|
||||
YearSelectProps
|
||||
>(function YearSelect({ className, ...otherProps }: YearSelectProps, ref) {
|
||||
return (
|
||||
<SubframeCore.DropdownMenu.Root>
|
||||
<SubframeCore.DropdownMenu.Trigger asChild={true}>
|
||||
<DatePicker.Select className={className} ref={ref} {...otherProps}>
|
||||
2026
|
||||
</DatePicker.Select>
|
||||
</SubframeCore.DropdownMenu.Trigger>
|
||||
<SubframeCore.DropdownMenu.Portal>
|
||||
<SubframeCore.DropdownMenu.Content
|
||||
side="bottom"
|
||||
align="start"
|
||||
sideOffset={4}
|
||||
asChild={true}
|
||||
>
|
||||
<DropdownMenuSelect className="min-w-0">
|
||||
<div className="flex w-full flex-col items-start gap-0.5">
|
||||
<DatePicker.YearCell>2021</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>2022</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>2023</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>2024</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>2025</DatePicker.YearCell>
|
||||
<DatePicker.YearCell selected={true}>2026</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>2027</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>2028</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>2029</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>2030</DatePicker.YearCell>
|
||||
<DatePicker.YearCell>2031</DatePicker.YearCell>
|
||||
</div>
|
||||
</DropdownMenuSelect>
|
||||
</SubframeCore.DropdownMenu.Content>
|
||||
</SubframeCore.DropdownMenu.Portal>
|
||||
</SubframeCore.DropdownMenu.Root>
|
||||
);
|
||||
});
|
||||
|
||||
export interface DayProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
selected?: boolean;
|
||||
today?: boolean;
|
||||
outside?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Day = React.forwardRef<HTMLDivElement, DayProps>(function Day(
|
||||
{
|
||||
children,
|
||||
selected = false,
|
||||
today = false,
|
||||
outside = false,
|
||||
className,
|
||||
...otherProps
|
||||
}: DayProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/3262f2d3 flex h-full max-h-[36px] min-h-[36px] w-full min-w-[36px] max-w-[36px] cursor-pointer items-center justify-center rounded-round hover:bg-surface-neutral-tertiary",
|
||||
{
|
||||
"border border-solid border-border-strong": today,
|
||||
"bg-brand-brand-surface-solid-default": selected,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{children ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-small-regular-0 font-body-small-regular-0 text-text-neutral-primary",
|
||||
{
|
||||
"text-text-neutral-subtle": outside,
|
||||
"text-text-neutral-on-solid": selected,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export interface YearCellProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
selected?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const YearCell = React.forwardRef<HTMLDivElement, YearCellProps>(
|
||||
function YearCell(
|
||||
{ children, selected = false, className, ...otherProps }: YearCellProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/5db54e28 flex h-7 w-full cursor-pointer items-center rounded-sm px-2 group/yearcell hover:bg-surface-feedback-subtle-information-hover",
|
||||
{ "bg-brand-brand-surface-solid-default": selected },
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{children ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-small-medium-0 font-body-small-medium-0 text-text-neutral-primary",
|
||||
{
|
||||
"text-text-neutral-on-solid group-hover/5db54e28:text-text-neutral-primary":
|
||||
selected,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export interface DatePickerRootProps
|
||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, "placeholder"> {
|
||||
disabled?: boolean;
|
||||
error?: boolean;
|
||||
hasValue?: boolean;
|
||||
size?: "small" | "medium" | "large";
|
||||
fillContainer?: boolean;
|
||||
borderless?: boolean;
|
||||
noPadding?: boolean;
|
||||
showLabel?: boolean;
|
||||
label?: React.ReactNode;
|
||||
helpText?: React.ReactNode;
|
||||
message?: React.ReactNode;
|
||||
placeholder?: React.ReactNode;
|
||||
value?: React.ReactNode;
|
||||
month?: React.ReactNode;
|
||||
year?: React.ReactNode;
|
||||
boolean?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DatePickerRoot = React.forwardRef<HTMLDivElement, DatePickerRootProps>(
|
||||
function DatePickerRoot(
|
||||
{
|
||||
disabled = false,
|
||||
error = false,
|
||||
hasValue = false,
|
||||
size = "medium",
|
||||
fillContainer = false,
|
||||
borderless = false,
|
||||
noPadding = false,
|
||||
showLabel = false,
|
||||
label,
|
||||
helpText,
|
||||
message,
|
||||
placeholder,
|
||||
value,
|
||||
month,
|
||||
year,
|
||||
boolean = false,
|
||||
className,
|
||||
...otherProps
|
||||
}: DatePickerRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<SubframeCore.DropdownMenu.Root>
|
||||
<SubframeCore.DropdownMenu.Trigger asChild={true}>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/e0461776 flex min-w-[144px] cursor-pointer flex-col items-start gap-3",
|
||||
{
|
||||
"gap-0": showLabel,
|
||||
"w-full min-w-[240px]": fillContainer,
|
||||
"cursor-not-allowed": disabled,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden flex-col items-start gap-2",
|
||||
{ flex: showLabel }
|
||||
)}
|
||||
>
|
||||
{label ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-default-medium-0 font-body-default-medium-0 text-text-neutral-primary",
|
||||
{
|
||||
"text-text-feedback-negative": error,
|
||||
"text-text-neutral-disabled": disabled,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
) : null}
|
||||
{helpText ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-small-regular-0 font-body-small-regular-0 text-text-neutral-tertiary",
|
||||
{
|
||||
"text-text-feedback-negative": error,
|
||||
"text-text-neutral-disabled": disabled,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{helpText}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full flex-col items-start gap-3",
|
||||
{ "gap-1": showLabel }
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full flex-col items-start gap-1 relative",
|
||||
{ "px-0 pt-1.5 pb-0": showLabel }
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex h-9 w-full flex-none items-center gap-2 rounded-sm border border-solid border-border-strong bg-surface-neutral-primary px-3 group-hover/e0461776:bg-surface-neutral-secondary group-focus-within/e0461776:bg-surface-neutral-secondary group-focus-within/e0461776:shadow-focus",
|
||||
{
|
||||
"border border-solid border-border-default": showLabel,
|
||||
"h-auto px-0 py-0": noPadding,
|
||||
"border-none": borderless,
|
||||
"h-12": size === "large",
|
||||
"h-8": size === "small",
|
||||
"border border-solid border-border-negative-solid group-focus-within/e0461776:border-2 group-focus-within/e0461776:border-solid group-focus-within/e0461776:border-border-negative-solid group-focus-within/e0461776:shadow-none":
|
||||
error,
|
||||
"border border-solid border-border-disabled bg-surface-neutral-disabled cursor-not-allowed group-hover/e0461776:bg-surface-neutral-disabled group-focus-within/e0461776:bg-surface-neutral-disabled group-focus-within/e0461776:shadow-none":
|
||||
disabled,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{value ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden grow shrink-0 basis-0 text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-secondary",
|
||||
{
|
||||
"text-body-small-regular-0 font-body-small-regular-0":
|
||||
size === "small",
|
||||
inline: hasValue,
|
||||
"text-text-neutral-disabled": disabled,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{value}
|
||||
</span>
|
||||
) : null}
|
||||
{placeholder ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"grow shrink-0 basis-0 text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-subtle",
|
||||
{
|
||||
"text-body-small-regular-0 font-body-small-regular-0":
|
||||
size === "small",
|
||||
hidden: hasValue,
|
||||
"text-text-neutral-disabled": disabled,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{placeholder}
|
||||
</span>
|
||||
) : null}
|
||||
<FeatherCalendar
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-default-regular-0 font-body-default-regular-0 text-icon-neutral-tertiary",
|
||||
{
|
||||
"text-body-small-regular-0 font-body-small-regular-0":
|
||||
size === "small",
|
||||
"text-icon-feedback-negative": error,
|
||||
"text-icon-neutral-disabled": disabled,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{message ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden text-body-extra-small-regular-0 font-body-extra-small-regular-0 text-text-neutral-tertiary",
|
||||
{
|
||||
inline: showLabel,
|
||||
"text-text-feedback-negative": error,
|
||||
"text-text-neutral-disabled": disabled,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{message}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</SubframeCore.DropdownMenu.Trigger>
|
||||
<SubframeCore.DropdownMenu.Portal>
|
||||
<SubframeCore.DropdownMenu.Content
|
||||
side="bottom"
|
||||
align="start"
|
||||
sideOffset={4}
|
||||
asChild={true}
|
||||
>
|
||||
<DropdownMenuSelect className="h-auto w-auto flex-none">
|
||||
<CalendarPanel month="Februar" year="2025" />
|
||||
</DropdownMenuSelect>
|
||||
</SubframeCore.DropdownMenu.Content>
|
||||
</SubframeCore.DropdownMenu.Portal>
|
||||
</SubframeCore.DropdownMenu.Root>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const DatePicker = Object.assign(DatePickerRoot, {
|
||||
Weekday,
|
||||
Select,
|
||||
CalendarPanel,
|
||||
MonthSelect,
|
||||
YearSelect,
|
||||
Day,
|
||||
YearCell,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { DatePicker as DatePickerComponent } from "./DatePicker";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const DatePicker = DatePickerComponent;
|
||||
@@ -0,0 +1,85 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Dialog — https://app.subframe.com/f023064a5ca0/library?component=Dialog_ca59db17-43fb-4247-8094-3c55162e902d
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface ContentProps
|
||||
extends React.ComponentProps<typeof SubframeCore.Dialog.Content> {
|
||||
children?: React.ReactNode;
|
||||
size?: "xs" | "small" | "medium" | "large" | "media";
|
||||
padding?: "default" | "spacious";
|
||||
hideCloseIcon?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Content = React.forwardRef<HTMLDivElement, ContentProps>(function Content(
|
||||
{
|
||||
children,
|
||||
size = "small",
|
||||
padding = "default",
|
||||
hideCloseIcon = false,
|
||||
className,
|
||||
...otherProps
|
||||
}: ContentProps,
|
||||
ref
|
||||
) {
|
||||
return children ? (
|
||||
<SubframeCore.Dialog.Content asChild={true} {...otherProps}>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/04ea6d47 flex w-full flex-col items-start gap-6 rounded-md border border-solid border-border-default bg-surface-neutral-secondary px-4 py-4 shadow-lg overflow-auto max-w-[560px] mobile:overflow-auto mobile:max-h-[90vh] mobile:max-w-[calc(100vw-32px)]",
|
||||
{
|
||||
"hide-close-icon": hideCloseIcon,
|
||||
"px-8 py-8": padding === "spacious",
|
||||
"h-192 w-320 max-w-320": size === "media",
|
||||
"max-w-[1200px]": size === "large",
|
||||
"max-w-[720px]": size === "medium",
|
||||
"max-w-[420px]": size === "xs",
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</SubframeCore.Dialog.Content>
|
||||
) : null;
|
||||
});
|
||||
|
||||
export interface DialogRootProps
|
||||
extends React.ComponentProps<typeof SubframeCore.Dialog.Root> {
|
||||
children?: React.ReactNode;
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DialogRoot = React.forwardRef<HTMLDivElement, DialogRootProps>(
|
||||
function DialogRoot(
|
||||
{ children, className, ...otherProps }: DialogRootProps,
|
||||
ref
|
||||
) {
|
||||
return children ? (
|
||||
<SubframeCore.Dialog.Root asChild={true} {...otherProps}>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex h-full w-full flex-col items-center justify-center gap-2 bg-surface-neutral-overlay",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</SubframeCore.Dialog.Root>
|
||||
) : null;
|
||||
}
|
||||
);
|
||||
|
||||
export const Dialog = Object.assign(DialogRoot, {
|
||||
Content,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { Dialog as DialogComponent } from "./Dialog";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const Dialog = DialogComponent;
|
||||
@@ -0,0 +1,74 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Dialog Fullscreen — https://app.subframe.com/f023064a5ca0/library?component=Dialog+Fullscreen_3f094173-71de-4378-a09a-05c482f7a137
|
||||
* Icon Button — https://app.subframe.com/f023064a5ca0/library?component=Icon+Button_af9405b1-8c54-4e01-9786-5aad308224f6
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherX } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
import { IconButton } from "../IconButton";
|
||||
|
||||
export interface DialogFullscreenRootProps
|
||||
extends React.ComponentProps<typeof SubframeCore.FullScreenDialog.Root> {
|
||||
children?: React.ReactNode;
|
||||
padding?: "default" | "spacious";
|
||||
hideCloseIcon?: boolean;
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DialogFullscreenRoot = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
DialogFullscreenRootProps
|
||||
>(function DialogFullscreenRoot(
|
||||
{
|
||||
children,
|
||||
padding = "default",
|
||||
hideCloseIcon = false,
|
||||
className,
|
||||
...otherProps
|
||||
}: DialogFullscreenRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<SubframeCore.FullScreenDialog.Root asChild={true} {...otherProps}>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/3f094173 flex h-full w-full flex-col items-center justify-center bg-surface-neutral-overlay px-6 py-6 mobile:px-4 mobile:py-4",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full grow shrink-0 basis-0 flex-col items-start overflow-hidden rounded-lg border border-solid border-border-default bg-surface-neutral-secondary shadow-lg group/dialogfs max-w-[2512px] relative",
|
||||
{ "hide-close-icon": hideCloseIcon }
|
||||
)}
|
||||
>
|
||||
{children ? (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full grow shrink-0 basis-0 flex-col items-start gap-4 px-4 py-4 overflow-auto",
|
||||
{ "px-8 py-8": padding === "spacious" }
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
) : null}
|
||||
<IconButton
|
||||
className="group-[.hide-close-icon]/dialogfs:hidden absolute right-4 top-4"
|
||||
variant="ghost"
|
||||
size="small"
|
||||
icon={<FeatherX />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</SubframeCore.FullScreenDialog.Root>
|
||||
);
|
||||
});
|
||||
|
||||
export const DialogFullscreen = DialogFullscreenRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { DialogFullscreen as DialogFullscreenComponent } from "./DialogFullscreen";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const DialogFullscreen = DialogFullscreenComponent;
|
||||
@@ -0,0 +1,67 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Drawer Right — https://app.subframe.com/f023064a5ca0/library?component=Drawer+Right_1e71b2cb-8d72-4e67-b368-8805179e9444
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface ContentProps
|
||||
extends React.ComponentProps<typeof SubframeCore.Drawer.Content> {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Content = React.forwardRef<HTMLDivElement, ContentProps>(function Content(
|
||||
{ children, className, ...otherProps }: ContentProps,
|
||||
ref
|
||||
) {
|
||||
return children ? (
|
||||
<SubframeCore.Drawer.Content asChild={true} {...otherProps}>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex h-full min-w-[320px] flex-col items-start gap-2 border-l border-solid border-[#e5e5e5] bg-[#ffffff]",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</SubframeCore.Drawer.Content>
|
||||
) : null;
|
||||
});
|
||||
|
||||
export interface DrawerRightRootProps
|
||||
extends React.ComponentProps<typeof SubframeCore.Drawer.Root> {
|
||||
children?: React.ReactNode;
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DrawerRightRoot = React.forwardRef<HTMLDivElement, DrawerRightRootProps>(
|
||||
function DrawerRightRoot(
|
||||
{ children, className, ...otherProps }: DrawerRightRootProps,
|
||||
ref
|
||||
) {
|
||||
return children ? (
|
||||
<SubframeCore.Drawer.Root asChild={true} {...otherProps}>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex h-full w-full flex-col items-end justify-center gap-2 bg-[#00000066]",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</SubframeCore.Drawer.Root>
|
||||
) : null;
|
||||
}
|
||||
);
|
||||
|
||||
export const DrawerRight = Object.assign(DrawerRightRoot, {
|
||||
Content,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { DrawerRight as DrawerRightComponent } from "./DrawerRight";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const DrawerRight = DrawerRightComponent;
|
||||
@@ -0,0 +1,379 @@
|
||||
// @subframe/sync-disable
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Accordion — https://app.subframe.com/f023064a5ca0/library?component=Accordion_d2e81e20-863a-4027-826a-991d8910efd9
|
||||
* Avatar — https://app.subframe.com/f023064a5ca0/library?component=Avatar_bec25ae6-5010-4485-b46b-cf79e3943ab2
|
||||
* Checkbox — https://app.subframe.com/f023064a5ca0/library?component=Checkbox_3816e3b5-c48c-499b-b45e-0777c6972523
|
||||
* Dropdown Menu multi select — https://app.subframe.com/f023064a5ca0/library?component=Dropdown+Menu+multi+select_8c9fd450-3912-4c8a-abae-4f24e3022a64
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherLayers } from "@subframe/core";
|
||||
import { FeatherSearch } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
import { Accordion } from "../Accordion";
|
||||
import { Avatar } from "../Avatar";
|
||||
import { Checkbox } from "../Checkbox";
|
||||
|
||||
export interface DropdownDividerProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DropdownDivider = React.forwardRef<HTMLDivElement, DropdownDividerProps>(
|
||||
function DropdownDivider(
|
||||
{ className, ...otherProps }: DropdownDividerProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full items-start gap-2 px-1 py-1",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className="flex h-px grow shrink-0 basis-0 flex-col items-center gap-2 bg-border-default" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export interface DropdownItemProps
|
||||
extends React.ComponentProps<typeof SubframeCore.DropdownMenu.Item> {
|
||||
children?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
iconRight?: React.ReactNode;
|
||||
image?: string;
|
||||
selected?: boolean;
|
||||
showCheckbox?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DropdownItem = React.forwardRef<HTMLDivElement, DropdownItemProps>(
|
||||
function DropdownItem(
|
||||
{
|
||||
children,
|
||||
icon = null,
|
||||
iconRight = null,
|
||||
image,
|
||||
selected = false,
|
||||
showCheckbox = false,
|
||||
className,
|
||||
...otherProps
|
||||
}: DropdownItemProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<SubframeCore.DropdownMenu.Item asChild={true} {...otherProps}>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/744235d7 flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-2.5 group/dropdown-item hover:bg-surface-neutral-tertiary active:bg-surface-neutral-secondary",
|
||||
{
|
||||
"bg-surface-ui-subtle-blue-default hover:bg-surface-ui-subtle-blue-hover active:bg-surface-ui-subtle-blue-active":
|
||||
selected,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames("hidden items-start", {
|
||||
flex: showCheckbox,
|
||||
})}
|
||||
>
|
||||
<Checkbox
|
||||
checked={selected}
|
||||
className="h-auto w-auto flex-none self-stretch"
|
||||
label="label"
|
||||
description="Description"
|
||||
helpText=""
|
||||
hideLabel={true}
|
||||
/>
|
||||
</div>
|
||||
{image ? (
|
||||
<img
|
||||
className="h-6 w-6 flex-none rounded-round object-cover"
|
||||
src={image}
|
||||
/>
|
||||
) : null}
|
||||
{icon ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-small-regular-150 font-body-small-regular-150 text-icon-neutral-tertiary",
|
||||
{ "text-icon-neutral-primary": selected }
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
{children ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"line-clamp-1 grow shrink-0 basis-0 text-body-small-regular-0 font-body-small-regular-0 text-text-neutral-tertiary",
|
||||
{ "text-text-neutral-primary": selected }
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
{iconRight ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-small-regular-150 font-body-small-regular-150 text-icon-neutral-tertiary",
|
||||
{ "text-icon-neutral-primary": selected }
|
||||
)}
|
||||
>
|
||||
{iconRight}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
</div>
|
||||
</SubframeCore.DropdownMenu.Item>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export interface DropdownUserItemProps
|
||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
||||
title?: React.ReactNode;
|
||||
subtitle?: React.ReactNode;
|
||||
image?: string;
|
||||
selected?: boolean;
|
||||
showCheckbox?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DropdownUserItem = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
DropdownUserItemProps
|
||||
>(function DropdownUserItem(
|
||||
{
|
||||
title,
|
||||
subtitle,
|
||||
image,
|
||||
selected = false,
|
||||
showCheckbox = false,
|
||||
className,
|
||||
...otherProps
|
||||
}: DropdownUserItemProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/ad6eaaaf flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-2 group/user-item hover:bg-surface-neutral-tertiary active:bg-surface-neutral-secondary",
|
||||
{
|
||||
"bg-surface-ui-subtle-blue-default hover:bg-surface-ui-subtle-blue-hover active:bg-surface-ui-subtle-blue-active":
|
||||
selected,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames("hidden items-start", {
|
||||
flex: showCheckbox,
|
||||
})}
|
||||
>
|
||||
<Checkbox
|
||||
checked={selected}
|
||||
label="label"
|
||||
description="Description"
|
||||
helpText=""
|
||||
hideLabel={true}
|
||||
/>
|
||||
</div>
|
||||
<Avatar size="small" image={image}>
|
||||
OR
|
||||
</Avatar>
|
||||
<div className="flex grow shrink-0 basis-0 flex-col items-start">
|
||||
{title ? (
|
||||
<span className="line-clamp-1 w-full text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-primary">
|
||||
{title}
|
||||
</span>
|
||||
) : null}
|
||||
{subtitle ? (
|
||||
<span className="line-clamp-1 w-full text-body-extra-small-regular-150 font-body-extra-small-regular-150 text-text-neutral-tertiary">
|
||||
{subtitle}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export interface DropdownCustomItemProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
selected?: boolean;
|
||||
showCheckbox?: boolean;
|
||||
size?: "medium" | "small" | "large";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DropdownCustomItem = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
DropdownCustomItemProps
|
||||
>(function DropdownCustomItem(
|
||||
{
|
||||
children,
|
||||
selected = false,
|
||||
showCheckbox = false,
|
||||
size = "medium",
|
||||
className,
|
||||
...otherProps
|
||||
}: DropdownCustomItemProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/e2891f3f flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-2.5 hover:bg-surface-neutral-tertiary active:bg-surface-neutral-secondary",
|
||||
{
|
||||
"px-2.5 py-3": size === "large",
|
||||
"px-1 py-1": size === "small",
|
||||
"bg-surface-ui-subtle-blue-default hover:bg-surface-ui-subtle-blue-hover active:bg-surface-ui-subtle-blue-active":
|
||||
selected,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<Checkbox
|
||||
className={SubframeUtils.twClassNames("hidden", { flex: showCheckbox })}
|
||||
label="label"
|
||||
description="Description"
|
||||
helpText=""
|
||||
hideLabel={true}
|
||||
/>
|
||||
{children ? (
|
||||
<div className="flex grow shrink-0 basis-0 flex-col items-start">
|
||||
{children}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export interface DropdownNestedItemProps
|
||||
extends React.ComponentProps<typeof Accordion> {
|
||||
children?: React.ReactNode;
|
||||
label?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DropdownNestedItem = React.forwardRef<
|
||||
React.ElementRef<typeof Accordion>,
|
||||
DropdownNestedItemProps
|
||||
>(function DropdownNestedItem(
|
||||
{
|
||||
children,
|
||||
label,
|
||||
icon = <FeatherLayers />,
|
||||
className,
|
||||
...otherProps
|
||||
}: DropdownNestedItemProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<Accordion
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/e3a3cd0a cursor-pointer group/nested-item",
|
||||
className
|
||||
)}
|
||||
variant="ghost"
|
||||
trigger={
|
||||
<div className="flex h-8 w-full flex-none items-center gap-2 rounded-md px-3 group-hover/e3a3cd0a:bg-surface-neutral-tertiary">
|
||||
{icon ? (
|
||||
<SubframeCore.IconWrapper className="text-body-small-regular-150 font-body-small-regular-150 text-icon-neutral-primary">
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
{label ? (
|
||||
<span className="line-clamp-1 grow shrink-0 basis-0 text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-primary">
|
||||
{label}
|
||||
</span>
|
||||
) : null}
|
||||
<Accordion.Chevron />
|
||||
</div>
|
||||
}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{children ? (
|
||||
<div className="flex w-full flex-col items-start gap-1">{children}</div>
|
||||
) : null}
|
||||
</Accordion>
|
||||
);
|
||||
});
|
||||
|
||||
export interface DropdownMenuMultiSelectRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
showSearch?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DropdownMenuMultiSelectRoot = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
DropdownMenuMultiSelectRootProps
|
||||
>(function DropdownMenuMultiSelectRoot(
|
||||
{
|
||||
children,
|
||||
showSearch = false,
|
||||
className,
|
||||
...otherProps
|
||||
}: DropdownMenuMultiSelectRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/8c9fd450 flex min-w-[192px] flex-col items-start rounded-md border border-solid border-border-default bg-surface-neutral-secondary px-1 py-1 shadow-lg",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden w-full flex-col items-start gap-1 pb-1",
|
||||
{ flex: showSearch }
|
||||
)}
|
||||
>
|
||||
<div className="flex w-full items-center gap-2 rounded-sm bg-surface-neutral-tertiary px-2 py-1.5">
|
||||
<FeatherSearch className="text-body-small-regular-150 font-body-small-regular-150 text-icon-neutral-tertiary" />
|
||||
<input
|
||||
className="min-w-[0px] grow shrink-0 basis-0 self-stretch text-body-small-regular-150 font-body-small-regular-150 text-text-neutral-secondary outline-none placeholder:text-text-neutral-subtle"
|
||||
placeholder="Suchen…"
|
||||
value=""
|
||||
type="search"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex h-px w-full flex-none flex-col items-center gap-2 bg-border-default" />
|
||||
</div>
|
||||
{children ? (
|
||||
<div className="flex w-full flex-col items-start gap-0.5">
|
||||
{children}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const DropdownMenuMultiSelect = Object.assign(
|
||||
DropdownMenuMultiSelectRoot,
|
||||
{
|
||||
DropdownDivider,
|
||||
DropdownItem,
|
||||
DropdownUserItem,
|
||||
DropdownCustomItem,
|
||||
DropdownNestedItem,
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { DropdownMenuMultiSelect as DropdownMenuMultiSelectComponent } from "./DropdownMenuMultiSelect";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const DropdownMenuMultiSelect = DropdownMenuMultiSelectComponent;
|
||||
@@ -0,0 +1,373 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Accordion — https://app.subframe.com/f023064a5ca0/library?component=Accordion_d2e81e20-863a-4027-826a-991d8910efd9
|
||||
* Avatar — https://app.subframe.com/f023064a5ca0/library?component=Avatar_bec25ae6-5010-4485-b46b-cf79e3943ab2
|
||||
* Checkbox — https://app.subframe.com/f023064a5ca0/library?component=Checkbox_3816e3b5-c48c-499b-b45e-0777c6972523
|
||||
* Dropdown Menu select — https://app.subframe.com/f023064a5ca0/library?component=Dropdown+Menu+select_99951515-459b-4286-919e-a89e7549b43b
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherLayers } from "@subframe/core";
|
||||
import { FeatherSearch } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
import { Accordion } from "../Accordion";
|
||||
import { Avatar } from "../Avatar";
|
||||
import { Checkbox } from "../Checkbox";
|
||||
|
||||
export interface DropdownDividerProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DropdownDivider = React.forwardRef<HTMLDivElement, DropdownDividerProps>(
|
||||
function DropdownDivider(
|
||||
{ className, ...otherProps }: DropdownDividerProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full items-start gap-2 px-1 py-1",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className="flex h-px grow shrink-0 basis-0 flex-col items-center gap-2 bg-border-default" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export interface DropdownItemProps
|
||||
extends React.ComponentProps<typeof SubframeCore.DropdownMenu.Item> {
|
||||
children?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
iconRight?: React.ReactNode;
|
||||
image?: string;
|
||||
selected?: boolean;
|
||||
showCheckbox?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DropdownItem = React.forwardRef<HTMLDivElement, DropdownItemProps>(
|
||||
function DropdownItem(
|
||||
{
|
||||
children,
|
||||
icon = null,
|
||||
iconRight = null,
|
||||
image,
|
||||
selected = false,
|
||||
showCheckbox = false,
|
||||
className,
|
||||
...otherProps
|
||||
}: DropdownItemProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<SubframeCore.DropdownMenu.Item asChild={true} {...otherProps}>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/adcae8d6 flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-2.5 group/dropdown-item hover:bg-surface-neutral-tertiary active:bg-surface-neutral-secondary",
|
||||
{
|
||||
"bg-surface-ui-subtle-blue-default hover:bg-surface-ui-subtle-blue-hover active:bg-surface-ui-subtle-blue-active":
|
||||
selected,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames("hidden items-start", {
|
||||
flex: showCheckbox,
|
||||
})}
|
||||
>
|
||||
<Checkbox
|
||||
className="h-auto w-auto flex-none self-stretch"
|
||||
label="label"
|
||||
description="Description"
|
||||
helpText=""
|
||||
hideLabel={true}
|
||||
/>
|
||||
</div>
|
||||
{image ? (
|
||||
<img
|
||||
className="h-6 w-6 flex-none rounded-round object-cover"
|
||||
src={image}
|
||||
/>
|
||||
) : null}
|
||||
{icon ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-small-regular-150 font-body-small-regular-150 text-icon-neutral-tertiary",
|
||||
{ "text-icon-neutral-primary": selected }
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
{children ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"line-clamp-1 grow shrink-0 basis-0 text-body-small-regular-0 font-body-small-regular-0 text-text-neutral-tertiary",
|
||||
{ "text-text-neutral-primary": selected }
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
{iconRight ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-small-regular-150 font-body-small-regular-150 text-icon-neutral-tertiary",
|
||||
{ "text-icon-neutral-primary": selected }
|
||||
)}
|
||||
>
|
||||
{iconRight}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
</div>
|
||||
</SubframeCore.DropdownMenu.Item>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export interface DropdownUserItemProps
|
||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
||||
title?: React.ReactNode;
|
||||
subtitle?: React.ReactNode;
|
||||
image?: string;
|
||||
selected?: boolean;
|
||||
showCheckbox?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DropdownUserItem = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
DropdownUserItemProps
|
||||
>(function DropdownUserItem(
|
||||
{
|
||||
title,
|
||||
subtitle,
|
||||
image,
|
||||
selected = false,
|
||||
showCheckbox = false,
|
||||
className,
|
||||
...otherProps
|
||||
}: DropdownUserItemProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/0d3c57ad flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-2 group/user-item hover:bg-surface-neutral-tertiary active:bg-surface-neutral-secondary",
|
||||
{
|
||||
"bg-surface-ui-subtle-blue-default hover:bg-surface-ui-subtle-blue-hover active:bg-surface-ui-subtle-blue-active":
|
||||
selected,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames("hidden items-start", {
|
||||
flex: showCheckbox,
|
||||
})}
|
||||
>
|
||||
<Checkbox
|
||||
label="label"
|
||||
description="Description"
|
||||
helpText=""
|
||||
hideLabel={true}
|
||||
/>
|
||||
</div>
|
||||
<Avatar size="small" image={image}>
|
||||
OR
|
||||
</Avatar>
|
||||
<div className="flex grow shrink-0 basis-0 flex-col items-start">
|
||||
{title ? (
|
||||
<span className="line-clamp-1 w-full text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-primary">
|
||||
{title}
|
||||
</span>
|
||||
) : null}
|
||||
{subtitle ? (
|
||||
<span className="line-clamp-1 w-full text-body-extra-small-regular-150 font-body-extra-small-regular-150 text-text-neutral-tertiary">
|
||||
{subtitle}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export interface DropdownCustomItemProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
selected?: boolean;
|
||||
showCheckbox?: boolean;
|
||||
size?: "medium" | "small" | "large";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DropdownCustomItem = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
DropdownCustomItemProps
|
||||
>(function DropdownCustomItem(
|
||||
{
|
||||
children,
|
||||
selected = false,
|
||||
showCheckbox = false,
|
||||
size = "medium",
|
||||
className,
|
||||
...otherProps
|
||||
}: DropdownCustomItemProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/4abe03f9 flex w-full cursor-pointer items-center gap-2 rounded-md px-2 py-2.5 hover:bg-surface-neutral-tertiary active:bg-surface-neutral-secondary",
|
||||
{
|
||||
"px-2.5 py-3": size === "large",
|
||||
"px-1 py-1": size === "small",
|
||||
"bg-surface-ui-subtle-blue-default hover:bg-surface-ui-subtle-blue-hover active:bg-surface-ui-subtle-blue-active":
|
||||
selected,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<Checkbox
|
||||
className={SubframeUtils.twClassNames("hidden", { flex: showCheckbox })}
|
||||
label="label"
|
||||
description="Description"
|
||||
helpText=""
|
||||
hideLabel={true}
|
||||
/>
|
||||
{children ? (
|
||||
<div className="flex grow shrink-0 basis-0 flex-col items-start">
|
||||
{children}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export interface DropdownNestedItemProps
|
||||
extends React.ComponentProps<typeof Accordion> {
|
||||
children?: React.ReactNode;
|
||||
label?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DropdownNestedItem = React.forwardRef<
|
||||
React.ElementRef<typeof Accordion>,
|
||||
DropdownNestedItemProps
|
||||
>(function DropdownNestedItem(
|
||||
{
|
||||
children,
|
||||
label,
|
||||
icon = <FeatherLayers />,
|
||||
className,
|
||||
...otherProps
|
||||
}: DropdownNestedItemProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<Accordion
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/ca3b20a3 cursor-pointer group/nested-item",
|
||||
className
|
||||
)}
|
||||
variant="ghost"
|
||||
trigger={
|
||||
<div className="flex h-8 w-full flex-none items-center gap-2 rounded-md px-3 group-hover/ca3b20a3:bg-surface-neutral-tertiary">
|
||||
{icon ? (
|
||||
<SubframeCore.IconWrapper className="text-body-small-regular-150 font-body-small-regular-150 text-icon-neutral-primary">
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
{label ? (
|
||||
<span className="line-clamp-1 grow shrink-0 basis-0 text-body-small-medium-150 font-body-small-medium-150 text-text-neutral-primary">
|
||||
{label}
|
||||
</span>
|
||||
) : null}
|
||||
<Accordion.Chevron />
|
||||
</div>
|
||||
}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{children ? (
|
||||
<div className="flex w-full flex-col items-start gap-1">{children}</div>
|
||||
) : null}
|
||||
</Accordion>
|
||||
);
|
||||
});
|
||||
|
||||
export interface DropdownMenuSelectRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
children?: React.ReactNode;
|
||||
showSearch?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const DropdownMenuSelectRoot = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
DropdownMenuSelectRootProps
|
||||
>(function DropdownMenuSelectRoot(
|
||||
{
|
||||
children,
|
||||
showSearch = false,
|
||||
className,
|
||||
...otherProps
|
||||
}: DropdownMenuSelectRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/99951515 flex min-w-[192px] flex-col items-start rounded-md border border-solid border-border-default bg-surface-neutral-secondary px-1 py-1 shadow-lg",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden w-full flex-col items-start gap-1 pb-1",
|
||||
{ flex: showSearch }
|
||||
)}
|
||||
>
|
||||
<div className="flex w-full items-center gap-2 rounded-sm bg-surface-neutral-tertiary px-2 py-1.5">
|
||||
<FeatherSearch className="text-body-small-regular-150 font-body-small-regular-150 text-icon-neutral-tertiary" />
|
||||
<input
|
||||
className="min-w-[0px] grow shrink-0 basis-0 self-stretch text-body-small-regular-150 font-body-small-regular-150 text-text-neutral-secondary outline-none placeholder:text-text-neutral-subtle"
|
||||
placeholder="Suchen…"
|
||||
value=""
|
||||
type="search"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex h-px w-full flex-none flex-col items-center gap-2 bg-border-default" />
|
||||
</div>
|
||||
{children ? (
|
||||
<div className="flex w-full flex-col items-start gap-0.5">
|
||||
{children}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const DropdownMenuSelect = Object.assign(DropdownMenuSelectRoot, {
|
||||
DropdownDivider,
|
||||
DropdownItem,
|
||||
DropdownUserItem,
|
||||
DropdownCustomItem,
|
||||
DropdownNestedItem,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { DropdownMenuSelect as DropdownMenuSelectComponent } from "./DropdownMenuSelect";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const DropdownMenuSelect = DropdownMenuSelectComponent;
|
||||
@@ -0,0 +1,109 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Button — https://app.subframe.com/f023064a5ca0/library?component=Button_3b777358-b86b-40af-9327-891efc6826fe
|
||||
* Empty State — https://app.subframe.com/f023064a5ca0/library?component=Empty+State_9fcf03a9-ada9-4a3d-80bc-87ff6ce78023
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
import { Button } from "../Button";
|
||||
|
||||
export interface EmptyStateRootProps
|
||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
||||
variant?: "default" | "no-results" | "error";
|
||||
outlined?: boolean;
|
||||
fillContainer?: boolean;
|
||||
title?: React.ReactNode;
|
||||
body?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const EmptyStateRoot = React.forwardRef<HTMLDivElement, EmptyStateRootProps>(
|
||||
function EmptyStateRoot(
|
||||
{
|
||||
variant = "default",
|
||||
outlined = false,
|
||||
fillContainer = false,
|
||||
title,
|
||||
body,
|
||||
className,
|
||||
...otherProps
|
||||
}: EmptyStateRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/9fcf03a9 flex max-w-[384px] items-center gap-4 px-4 py-8",
|
||||
{
|
||||
"max-w-none": fillContainer,
|
||||
"rounded-md border border-solid border-border-default bg-surface-neutral-primary":
|
||||
outlined,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className="flex flex-col items-center gap-6">
|
||||
<div className="flex flex-col items-center gap-7">
|
||||
<img
|
||||
className={SubframeUtils.twClassNames(
|
||||
"h-12 w-12 flex-none object-cover",
|
||||
{ hidden: variant === "error" || variant === "no-results" }
|
||||
)}
|
||||
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIHZpZXdCb3g9IjAgMCA0OCA0OCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBjbGFzcz0iZmxleC1ub25lIiBkYXRhLW5vZGUtaWQ9IjM3YmE4ZTQ2Ij48cGF0aCBkPSJNMzcuNSAyMVYzNy42NjY3QzM3LjUgMzguMDIwMyAzNy4zNTk1IDM4LjM1OTQgMzcuMTA5NSAzOC42MDk1QzM2Ljg1OTQgMzguODU5NSAzNi41MjAzIDM5IDM2LjE2NjcgMzlINy41QzcuMTAyMTggMzkgNi43MjA2NCAzOC44NDIgNi40MzkzNCAzOC41NjA3QzYuMTU4MDQgMzguMjc5NCA2IDM3Ljg5NzggNiAzNy41VjE2LjVDNiAxNi4xMDIyIDYuMTU4MDQgMTUuNzIwNiA2LjQzOTM0IDE1LjQzOTNDNi43MjA2NCAxNS4xNTggNy4xMDIxOCAxNSA3LjUgMTVIMTZDMTYuMzI0NiAxNSAxNi42NDA0IDE1LjEwNTMgMTYuOSAxNS4zTDIyLjEgMTkuMkMyMi4zNTk2IDE5LjM5NDcgMjIuNjc1NCAxOS41IDIzIDE5LjVIMzZDMzYuMzk3OCAxOS41IDM2Ljc3OTQgMTkuNjU4IDM3LjA2MDcgMTkuOTM5M0MzNy4zNDIgMjAuMjIwNiAzNy41IDIwLjYwMjIgMzcuNSAyMVoiIHN0cm9rZT0iIzg1ODU4OSIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgZGF0YS1ub2RlLWlkPSI4MzBkNDBlZi05N2E3LTRjMjYtODQwZi0yNDQzMTEwNjYwMjgiPjwvcGF0aD48cGF0aCBkPSJNMTIgMTVWMTAuNUMxMiAxMC4xMDIyIDEyLjE1OCA5LjcyMDY0IDEyLjQzOTMgOS40MzkzNEMxMi43MjA2IDkuMTU4MDQgMTMuMTAyMiA5IDEzLjUgOUgyMkMyMi4zMjQ2IDkgMjIuNjQwNCA5LjEwNTI3IDIyLjkgOS4zTDI4LjEgMTMuMkMyOC4zNTk2IDEzLjM5NDcgMjguNjc1NCAxMy41IDI5IDEzLjVINDJDNDIuMzk3OCAxMy41IDQyLjc3OTQgMTMuNjU4IDQzLjA2MDcgMTMuOTM5M0M0My4zNDIgMTQuMjIwNiA0My41IDE0LjYwMjIgNDMuNSAxNVYzMS42NjY3QzQzLjUgMzEuODQxOCA0My40NjU1IDMyLjAxNTEgNDMuMzk4NSAzMi4xNzY5QzQzLjMzMTUgMzIuMzM4NyA0My4yMzMzIDMyLjQ4NTcgNDMuMTA5NSAzMi42MDk1QzQyLjk4NTcgMzIuNzMzMyA0Mi44Mzg3IDMyLjgzMTUgNDIuNjc2OSAzMi44OTg1QzQyLjUxNTEgMzIuOTY1NSA0Mi4zNDE4IDMzIDQyLjE2NjcgMzNIMzcuNSIgc3Ryb2tlPSIjODU4NTg5IiBzdHJva2Utd2lkdGg9IjEuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBkYXRhLW5vZGUtaWQ9IjJiZDUxMmE0LWI3YzQtNDg5MS05N2NmLTliNjkyZTc1MTNkMSI+PC9wYXRoPjwvc3ZnPg=="
|
||||
/>
|
||||
<img
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden h-12 w-12 flex-none object-cover",
|
||||
{ block: variant === "no-results" }
|
||||
)}
|
||||
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIHZpZXdCb3g9IjAgMCA0OCA0OCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMjEuNzUgMzcuNUMzMC40NDg1IDM3LjUgMzcuNSAzMC40NDg1IDM3LjUgMjEuNzVDMzcuNSAxMy4wNTE1IDMwLjQ0ODUgNiAyMS43NSA2QzEzLjA1MTUgNiA2IDEzLjA1MTUgNiAyMS43NUM2IDMwLjQ0ODUgMTMuMDUxNSAzNy41IDIxLjc1IDM3LjVaIiBzdHJva2U9IiM4NTg1ODkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTMyLjg4NjMgMzIuODg2N0w0MS45OTg5IDQxLjk5OTMiIHN0cm9rZT0iIzg1ODU4OSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4="
|
||||
/>
|
||||
<img
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden h-12 w-12 flex-none object-cover",
|
||||
{ block: variant === "error" }
|
||||
)}
|
||||
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIHZpZXdCb3g9IjAgMCA0OCA0OCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNMjQgMTkuNVYyNyIgc3Ryb2tlPSIjQzUyMDIwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0yMS40MDM2IDcuNDk3MTNMNC45MDgwNCAzNS45OTU3QzQuNjQ0MDkgMzYuNDUxNyA0LjUwNDg5IDM2Ljk2OTIgNC41MDQ0NiAzNy40OTYxQzQuNTA0MDIgMzguMDIzIDQuNjQyMzYgMzguNTQwNyA0LjkwNTU1IDM4Ljk5NzFDNS4xNjg3NSAzOS40NTM2IDUuNTQ3NTEgMzkuODMyNyA2LjAwMzc0IDQwLjA5NjJDNi40NTk5NyA0MC4zNTk4IDYuOTc3NTcgNDAuNDk4NiA3LjUwNDQ3IDQwLjQ5ODZINDAuNDk1NkM0MS4wMjI0IDQwLjQ5ODYgNDEuNTQgNDAuMzU5OCA0MS45OTYzIDQwLjA5NjJDNDIuNDUyNSAzOS44MzI3IDQyLjgzMTMgMzkuNDUzNiA0My4wOTQ1IDM4Ljk5NzFDNDMuMzU3NyAzOC41NDA3IDQzLjQ5NiAzOC4wMjMgNDMuNDk1NiAzNy40OTYxQzQzLjQ5NTEgMzYuOTY5MiA0My4zNTU5IDM2LjQ1MTcgNDMuMDkyIDM1Ljk5NTdMMjYuNTk2NCA3LjQ5NzEzQzI2LjMzMjkgNy4wNDE4NSAyNS45NTQzIDYuNjYzODcgMjUuNDk4NiA2LjQwMTFDMjUuMDQyOSA2LjEzODMyIDI0LjUyNjEgNiAyNCA2QzIzLjQ3NCA2IDIyLjk1NzIgNi4xMzgzMiAyMi41MDE0IDYuNDAxMUMyMi4wNDU3IDYuNjYzODcgMjEuNjY3MSA3LjA0MTg1IDIxLjQwMzYgNy40OTcxM1Y3LjQ5NzEzWiIgc3Ryb2tlPSIjQzUyMDIwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0yNCAzNS42MjVDMjUuMDM1NSAzNS42MjUgMjUuODc1IDM0Ljc4NTUgMjUuODc1IDMzLjc1QzI1Ljg3NSAzMi43MTQ1IDI1LjAzNTUgMzEuODc1IDI0IDMxLjg3NUMyMi45NjQ1IDMxLjg3NSAyMi4xMjUgMzIuNzE0NSAyMi4xMjUgMzMuNzVDMjIuMTI1IDM0Ljc4NTUgMjIuOTY0NSAzNS42MjUgMjQgMzUuNjI1WiIgZmlsbD0iI0M1MjAyMCIvPjwvc3ZnPg=="
|
||||
/>
|
||||
<div className="flex w-full flex-col items-center gap-4 px-4">
|
||||
{title ? (
|
||||
<span className="w-full text-body-large-0 font-body-large-0 text-text-neutral-primary text-center">
|
||||
{title}
|
||||
</span>
|
||||
) : null}
|
||||
{body ? (
|
||||
<span className="break-words text-body-default-regular-150 font-body-default-regular-150 text-text-neutral-secondary text-center">
|
||||
{body}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<Button
|
||||
className={SubframeUtils.twClassNames("h-9 w-36 flex-none", {
|
||||
"w-28": variant === "error" || variant === "no-results",
|
||||
})}
|
||||
variant={
|
||||
variant === "error"
|
||||
? "tertiary"
|
||||
: variant === "no-results"
|
||||
? "tertiary"
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{variant === "error"
|
||||
? "Try again"
|
||||
: variant === "no-results"
|
||||
? "Clear filters"
|
||||
: "Create project"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const EmptyState = EmptyStateRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { EmptyState as EmptyStateComponent } from "./EmptyState";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const EmptyState = EmptyStateComponent;
|
||||
@@ -0,0 +1,81 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Button — https://app.subframe.com/f023064a5ca0/library?component=Button_3b777358-b86b-40af-9327-891efc6826fe
|
||||
* Error State — https://app.subframe.com/f023064a5ca0/library?component=Error+State_07f75db5-d9ee-406e-8c83-cb9e3bf687ee
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
import { Button } from "../Button";
|
||||
|
||||
export interface ErrorStateRootProps
|
||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
||||
variant?: "inline" | "full-width";
|
||||
hideCancel?: boolean;
|
||||
title?: React.ReactNode;
|
||||
description?: React.ReactNode;
|
||||
retryLabel?: React.ReactNode;
|
||||
cancelLabel?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const ErrorStateRoot = React.forwardRef<HTMLDivElement, ErrorStateRootProps>(
|
||||
function ErrorStateRoot(
|
||||
{
|
||||
variant = "inline",
|
||||
hideCancel = false,
|
||||
title,
|
||||
description,
|
||||
retryLabel,
|
||||
cancelLabel,
|
||||
className,
|
||||
...otherProps
|
||||
}: ErrorStateRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/07f75db5 flex min-w-[192px] max-w-[576px] flex-col items-start gap-2 rounded-md border-2 border-solid border-border-negative-solid bg-surface-feedback-subtle-negative px-4 py-4 shadow-sm group/error-state",
|
||||
{ "w-full min-w-0 max-w-none": variant === "full-width" },
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className="flex w-full flex-col items-start gap-6">
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<img
|
||||
className="h-1 w-1 flex-none object-cover"
|
||||
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNCIgaGVpZ2h0PSI0IiB2aWV3Qm94PSIwIDAgNCA0IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJoLTEgdy0xIGZsZXgtbm9uZSIgZGF0YS1ub2RlLWlkPSJjNWJhMzRjMCI+PGNpcmNsZSBjeD0iMiIgY3k9IjIiIHI9IjIiIGZpbGw9IiNDNTIwMjAiPjwvY2lyY2xlPjwvc3ZnPg=="
|
||||
/>
|
||||
{title ? (
|
||||
<span className="grow shrink-0 basis-0 whitespace-pre-wrap text-body-default-medium-0 font-body-default-medium-0 text-text-feedback-negative">
|
||||
{title}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex flex-col items-start gap-6 pl-3">
|
||||
{description ? (
|
||||
<span className="text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-secondary">
|
||||
{description}
|
||||
</span>
|
||||
) : null}
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="tertiary">{retryLabel}</Button>
|
||||
<Button
|
||||
className={SubframeUtils.twClassNames({ hidden: hideCancel })}
|
||||
variant="ghost"
|
||||
>
|
||||
{cancelLabel}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const ErrorState = ErrorStateRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { ErrorState as ErrorStateComponent } from "./ErrorState";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const ErrorState = ErrorStateComponent;
|
||||
@@ -0,0 +1,200 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Button — https://app.subframe.com/f023064a5ca0/library?component=Button_3b777358-b86b-40af-9327-891efc6826fe
|
||||
* File Upload — https://app.subframe.com/f023064a5ca0/library?component=File+Upload_28a3336a-2bdd-4d1c-8dc1-75c9d6eef452
|
||||
* Progress — https://app.subframe.com/f023064a5ca0/library?component=Progress_60964db0-a1bf-428b-b9d5-f34cdf58ea77
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherFile } from "@subframe/core";
|
||||
import { FeatherUpload } from "@subframe/core";
|
||||
import { FeatherX } from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
import { Button } from "../Button";
|
||||
import { Progress } from "../Progress";
|
||||
|
||||
export interface FileUploadRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
fullwith?: boolean;
|
||||
state?: "default" | "hover" | "uploading" | "error" | "uploaded";
|
||||
filename?: React.ReactNode;
|
||||
fileSize?: React.ReactNode;
|
||||
uploadProgress?: number;
|
||||
progressText?: React.ReactNode;
|
||||
errorMessage?: React.ReactNode;
|
||||
browseLabel?: React.ReactNode;
|
||||
cancelLabel?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const FileUploadRoot = React.forwardRef<HTMLDivElement, FileUploadRootProps>(
|
||||
function FileUploadRoot(
|
||||
{
|
||||
fullwith = false,
|
||||
state = "default",
|
||||
filename,
|
||||
fileSize,
|
||||
uploadProgress = 60,
|
||||
progressText,
|
||||
errorMessage,
|
||||
browseLabel,
|
||||
cancelLabel,
|
||||
className,
|
||||
...otherProps
|
||||
}: FileUploadRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/28a3336a flex flex-col items-start gap-2",
|
||||
{ "w-full": fullwith },
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex h-52 w-80 flex-none flex-col items-center justify-center gap-5 rounded-lg border border-dashed border-border-strong bg-surface-neutral-primary px-6 py-8",
|
||||
{
|
||||
hidden: state === "uploaded" || state === "uploading",
|
||||
"h-auto border border-dashed border-border-negative-solid":
|
||||
state === "error",
|
||||
"border-2 border-dashed border-border-information-solid bg-surface-feedback-subtle-information":
|
||||
state === "hover",
|
||||
"h-auto w-full": fullwith,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex flex-col items-center justify-center gap-8",
|
||||
{ "gap-6": state === "error" }
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex flex-col items-center justify-center gap-8",
|
||||
{ "gap-6": state === "hover" }
|
||||
)}
|
||||
>
|
||||
<FeatherUpload className="text-subtitle font-subtitle text-icon-neutral-secondary" />
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden text-body-small-medium-0 font-body-small-medium-0 text-text-feedback-link-default",
|
||||
{
|
||||
"text-text-neutral-primary": state === "error",
|
||||
"text-text-neutral-primary inline-flex": state === "hover",
|
||||
}
|
||||
)}
|
||||
>
|
||||
Drop your invoice file here
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-default-regular-0 font-body-default-regular-0 text-text-neutral-secondary",
|
||||
{ hidden: state === "hover" }
|
||||
)}
|
||||
>
|
||||
Drop your invoice file here or
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex flex-col items-center justify-center gap-5",
|
||||
{ "gap-6": state === "error", hidden: state === "hover" }
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex flex-col items-center justify-center gap-3",
|
||||
{ hidden: state === "hover" }
|
||||
)}
|
||||
>
|
||||
<Button variant="tertiary">Click here to browse</Button>
|
||||
</div>
|
||||
{errorMessage ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden text-body-small-regular-0 font-body-small-regular-0 text-text-feedback-negative",
|
||||
{ "inline-flex": state === "error" }
|
||||
)}
|
||||
>
|
||||
{errorMessage}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden w-80 flex-col items-start gap-4 rounded-lg border border-dashed border-border-strong bg-surface-neutral-primary px-6 py-8",
|
||||
{ flex: state === "uploading", "w-full flex-none": fullwith }
|
||||
)}
|
||||
>
|
||||
{filename ? (
|
||||
<span className="text-body-default-medium-0 font-body-default-medium-0 text-text-neutral-primary">
|
||||
{filename}
|
||||
</span>
|
||||
) : null}
|
||||
<Progress
|
||||
hideLabel={state === "uploading" ? true : undefined}
|
||||
label="Uploading file"
|
||||
statusText="30%"
|
||||
/>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden flex-col items-start gap-6",
|
||||
{ flex: state === "uploading" }
|
||||
)}
|
||||
>
|
||||
{progressText ? (
|
||||
<span className="text-body-small-regular-0 font-body-small-regular-0 text-text-neutral-tertiary">
|
||||
{progressText}
|
||||
</span>
|
||||
) : null}
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden items-center gap-2",
|
||||
{ flex: state === "uploading" }
|
||||
)}
|
||||
>
|
||||
<Button
|
||||
variant={state === "uploading" ? "destructive" : undefined}
|
||||
>
|
||||
{state === "uploading" ? "Cancel" : "Button"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden w-72 items-center gap-3 rounded-md border border-solid border-border-default bg-surface-neutral-primary px-3 py-4",
|
||||
{ flex: state === "uploaded", "w-full flex-none": fullwith }
|
||||
)}
|
||||
>
|
||||
<FeatherFile className="text-body-large font-body-large text-icon-neutral-secondary" />
|
||||
<div className="flex grow shrink-0 basis-0 flex-col items-start gap-2 self-stretch">
|
||||
{filename ? (
|
||||
<span className="text-body-small-medium-0 font-body-small-medium-0 text-text-neutral-primary">
|
||||
{filename}
|
||||
</span>
|
||||
) : null}
|
||||
{fileSize ? (
|
||||
<span className="text-body-small-regular-0 font-body-small-regular-0 text-text-neutral-tertiary">
|
||||
{fileSize}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex items-start">
|
||||
<FeatherX className="text-body-default-regular-0 font-body-default-regular-0 text-icon-neutral-subtle" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const FileUpload = FileUploadRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { FileUpload as FileUploadComponent } from "./FileUpload";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const FileUpload = FileUploadComponent;
|
||||
@@ -0,0 +1,212 @@
|
||||
// @subframe/sync-disable
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Filter Bar — https://app.subframe.com/f023064a5ca0/library?component=Filter+Bar_f2cf6974-3297-4da8-8613-cd00dc1c228e
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherBriefcase } from "@subframe/core";
|
||||
import { FeatherChevronDown } from "@subframe/core";
|
||||
import { FeatherRotateCcw } from "@subframe/core";
|
||||
import { FeatherSearch } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface StatusFilterButtonProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
icon?: React.ReactNode;
|
||||
label?: React.ReactNode;
|
||||
showCount?: boolean;
|
||||
count?: React.ReactNode;
|
||||
dropdown?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const StatusFilterButton = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
StatusFilterButtonProps
|
||||
>(function StatusFilterButton(
|
||||
{
|
||||
icon = <FeatherBriefcase />,
|
||||
label,
|
||||
showCount = false,
|
||||
count,
|
||||
dropdown,
|
||||
className,
|
||||
...otherProps
|
||||
}: StatusFilterButtonProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<SubframeCore.DropdownMenu.Root>
|
||||
<SubframeCore.DropdownMenu.Trigger asChild={true}>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/4ad576dd flex h-6 cursor-pointer items-center gap-0.5 rounded-md border border-solid border-border-default pl-1 pr-0.5 py-1 hover:bg-surface-neutral-tertiary focus-within:shadow-focus",
|
||||
{
|
||||
"border border-solid border-brand-brand-secondary-border-subtle-default bg-surface-feedback-subtle-information-hover pl-px hover:border hover:border-solid hover:border-brand-brand-secondary-border-subtle-hover hover:bg-surface-feedback-subtle-information-hover active:border active:border-solid active:border-brand-brand-secondary-border-subtle-active active:bg-surface-feedback-subtle-information-active focus-within:border focus-within:border-solid focus-within:border-brand-brand-secondary-border-subtle-hover":
|
||||
showCount,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden h-5 grow shrink-0 basis-0 items-center gap-1 rounded-sm bg-surface-neutral-secondary px-1.5 py-0.5",
|
||||
{ flex: showCount }
|
||||
)}
|
||||
>
|
||||
<div className="flex h-1 w-1 flex-none flex-col items-start gap-2 rounded-round bg-icon-neutral-primary" />
|
||||
{count ? (
|
||||
<span className="text-body-small-medium-0 font-body-small-medium-0 text-text-neutral-primary">
|
||||
{count}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
{icon ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-default-regular-150 font-body-default-regular-150 text-icon-neutral-tertiary",
|
||||
{ "text-icon-neutral-primary": showCount }
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
{label ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-small-regular-0 font-body-small-regular-0 text-text-neutral-tertiary",
|
||||
{ "text-text-neutral-primary": showCount }
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex items-center pt-px">
|
||||
<FeatherChevronDown
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-small-regular-150 font-body-small-regular-150 text-icon-neutral-tertiary",
|
||||
{ "text-icon-neutral-primary": showCount }
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</SubframeCore.DropdownMenu.Trigger>
|
||||
<SubframeCore.DropdownMenu.Portal>
|
||||
<SubframeCore.DropdownMenu.Content
|
||||
side="bottom"
|
||||
align="start"
|
||||
sideOffset={4}
|
||||
asChild={true}
|
||||
>
|
||||
{dropdown ? (
|
||||
<div className="items-start contents">{dropdown}</div>
|
||||
) : null}
|
||||
</SubframeCore.DropdownMenu.Content>
|
||||
</SubframeCore.DropdownMenu.Portal>
|
||||
</SubframeCore.DropdownMenu.Root>
|
||||
);
|
||||
});
|
||||
|
||||
export interface FilterBarRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
hideSearch?: boolean;
|
||||
showActiveFilters?: boolean;
|
||||
searchValue?: string;
|
||||
onSearchChange?: (value: string) => void;
|
||||
onClearFilters?: () => void;
|
||||
children?: React.ReactNode;
|
||||
activeFilters?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const FilterBarRoot = React.forwardRef<HTMLDivElement, FilterBarRootProps>(
|
||||
function FilterBarRoot(
|
||||
{
|
||||
hideSearch = false,
|
||||
showActiveFilters = false,
|
||||
searchValue,
|
||||
onSearchChange = () => {},
|
||||
onClearFilters = () => {},
|
||||
children,
|
||||
activeFilters,
|
||||
className,
|
||||
...otherProps
|
||||
}: FilterBarRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/f2cf6974 flex w-full flex-col items-start gap-2 rounded-md bg-surface-neutral-secondary px-3 py-2",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className="flex w-full flex-wrap items-center gap-1">
|
||||
<div
|
||||
className={SubframeUtils.twClassNames("flex items-center gap-1.5", {
|
||||
hidden: hideSearch,
|
||||
})}
|
||||
>
|
||||
<FeatherSearch className="text-body-small-regular-150 font-body-small-regular-150 text-icon-neutral-tertiary" />
|
||||
<input
|
||||
className="w-40 flex-none text-body-small-regular-150 font-body-small-regular-150 text-text-neutral-secondary outline-none placeholder:text-text-neutral-subtle"
|
||||
placeholder="Freitext suchen..."
|
||||
value={searchValue}
|
||||
onChange={(event) => onSearchChange(event.target.value)}
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex h-5 w-px flex-none items-start bg-border-default",
|
||||
{ hidden: hideSearch }
|
||||
)}
|
||||
/>
|
||||
{children ? (
|
||||
<div className="items-start contents">{children}</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden w-full flex-wrap items-center gap-1.5 border-t border-solid border-border-default pt-2",
|
||||
{ flex: showActiveFilters }
|
||||
)}
|
||||
>
|
||||
{activeFilters ? (
|
||||
<div className="items-start contents">{activeFilters}</div>
|
||||
) : null}
|
||||
<div
|
||||
className="flex h-6 items-center gap-1 rounded-md border border-solid border-border-negative-subtle bg-surface-feedback-subtle-negative px-1.5 py-1 cursor-pointer hover:bg-surface-feedback-subtle-negative-hover active:bg-surface-feedback-subtle-negative-active focus-within:shadow-focus"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={onClearFilters}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter" || event.key === " ") {
|
||||
event.preventDefault();
|
||||
onClearFilters();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<FeatherRotateCcw className="text-body-small-regular-150 font-body-small-regular-150 text-icon-feedback-negative" />
|
||||
<span className="whitespace-nowrap text-body-small-regular-0 font-body-small-regular-0 text-text-feedback-negative">
|
||||
Zurücksetzen
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const FilterBar = Object.assign(FilterBarRoot, {
|
||||
StatusFilterButton,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { FilterBar as FilterBarComponent } from "./FilterBar";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const FilterBar = FilterBarComponent;
|
||||
@@ -0,0 +1,55 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Form save Retry — https://app.subframe.com/f023064a5ca0/library?component=Form+save+Retry_db7627dd-19cb-457b-bbfd-6fe16d620fe0
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface FormSaveRetryRootProps
|
||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
||||
variant?: "default";
|
||||
title?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const FormSaveRetryRoot = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
FormSaveRetryRootProps
|
||||
>(function FormSaveRetryRoot(
|
||||
{
|
||||
variant = "default",
|
||||
title,
|
||||
children,
|
||||
className,
|
||||
...otherProps
|
||||
}: FormSaveRetryRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex max-w-[576px] flex-col items-start gap-2 px-4 py-4",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className="flex w-full items-center gap-2">
|
||||
<img
|
||||
className="h-1 w-1 flex-none object-cover"
|
||||
src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNCIgaGVpZ2h0PSI0IiB2aWV3Qm94PSIwIDAgNCA0IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGNsYXNzPSJoLTEgdy0xIGZsZXgtbm9uZSIgZGF0YS1ub2RlLWlkPSJjNWJhMzRjMCI+PGNpcmNsZSBjeD0iMiIgY3k9IjIiIHI9IjIiIGZpbGw9IiNDNTIwMjAiPjwvY2lyY2xlPjwvc3ZnPg=="
|
||||
/>
|
||||
{children ? (
|
||||
<div className="flex grow shrink-0 basis-0 items-center gap-2">
|
||||
{children}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const FormSaveRetry = FormSaveRetryRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { FormSaveRetry as FormSaveRetryComponent } from "./FormSaveRetry";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const FormSaveRetry = FormSaveRetryComponent;
|
||||
@@ -0,0 +1,134 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Breadcrumbs — https://app.subframe.com/f023064a5ca0/library?component=Breadcrumbs_8898334b-a66f-4ee8-8bd1-afcfa8e37cc0
|
||||
* Header Navigation — https://app.subframe.com/f023064a5ca0/library?component=Header+Navigation_9abee202-1896-4bb8-b899-63d76af2825e
|
||||
* Icon Button — https://app.subframe.com/f023064a5ca0/library?component=Icon+Button_af9405b1-8c54-4e01-9786-5aad308224f6
|
||||
* Search Input — https://app.subframe.com/f023064a5ca0/library?component=Search+Input_75a35ed3-52c0-4435-8586-e390eb77d723
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherBell } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
import { Breadcrumbs } from "../Breadcrumbs";
|
||||
import { IconButton } from "../IconButton";
|
||||
import { SearchInput } from "../SearchInput";
|
||||
|
||||
export interface NavItemProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
selected?: boolean;
|
||||
children?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const NavItem = React.forwardRef<HTMLDivElement, NavItemProps>(function NavItem(
|
||||
{
|
||||
selected = false,
|
||||
children,
|
||||
icon = null,
|
||||
className,
|
||||
...otherProps
|
||||
}: NavItemProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/448f9541 flex cursor-pointer items-center justify-center gap-2 rounded-sm px-3 py-1.5 hover:bg-surface-neutral-tertiary",
|
||||
{ "bg-surface-neutral-tertiary py-2": selected },
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{icon ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-small-medium-0 font-body-small-medium-0 text-icon-neutral-secondary",
|
||||
{ "text-icon-neutral-primary": selected }
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
{children ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"whitespace-nowrap text-body-small-medium-0 font-body-small-medium-0 text-text-neutral-secondary",
|
||||
{ "text-text-neutral-primary": selected }
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export interface HeaderNavigationRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
leftSlot?: React.ReactNode;
|
||||
breadcrumbs?: React.ReactNode[];
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const HeaderNavigationRoot = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
HeaderNavigationRootProps
|
||||
>(function HeaderNavigationRoot(
|
||||
{
|
||||
leftSlot,
|
||||
breadcrumbs = ["Übersicht"],
|
||||
className,
|
||||
...otherProps
|
||||
}: HeaderNavigationRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex h-14 w-full items-center justify-between border-b border-solid border-border-default bg-surface-neutral-secondary pl-6 pr-3 py-3 mobile:px-4",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className="flex grow shrink-0 basis-0 items-center justify-between">
|
||||
<div className="flex items-center gap-6 pr-1">
|
||||
<Breadcrumbs>
|
||||
{breadcrumbs.map((breadcrumb, index) => (
|
||||
<React.Fragment key={`${index}-${String(breadcrumb)}`}>
|
||||
{index > 0 ? <Breadcrumbs.Divider /> : null}
|
||||
<Breadcrumbs.Item active={index === breadcrumbs.length - 1}>
|
||||
{breadcrumb}
|
||||
</Breadcrumbs.Item>
|
||||
</React.Fragment>
|
||||
))}
|
||||
</Breadcrumbs>
|
||||
</div>
|
||||
{leftSlot ? (
|
||||
<div className="flex grow shrink-0 basis-0 items-center gap-6 mobile:gap-2">
|
||||
{leftSlot}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex items-center gap-2 self-stretch pl-1">
|
||||
<SearchInput
|
||||
className="h-auto grow shrink-0 basis-0"
|
||||
size="small"
|
||||
label="Label"
|
||||
helpText="Help text"
|
||||
message="Hilfetext"
|
||||
>
|
||||
<SearchInput.Input placeholder="Suche" />
|
||||
</SearchInput>
|
||||
<IconButton variant="ghost" size="small" icon={<FeatherBell />} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const HeaderNavigation = Object.assign(HeaderNavigationRoot, {
|
||||
NavItem,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { HeaderNavigation as HeaderNavigationComponent } from "./HeaderNavigation";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const HeaderNavigation = HeaderNavigationComponent;
|
||||
@@ -0,0 +1,87 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Help Circle — https://app.subframe.com/f023064a5ca0/library?component=Help+Circle_bdf8292f-ad00-49d7-9ca6-98c24f58f842
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherHelpCircle } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface HelpCircleRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
icon?: React.ReactNode;
|
||||
size?: "small" | "medium" | "large";
|
||||
shape?: "default" | "background";
|
||||
tooltipText?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const HelpCircleRoot = React.forwardRef<HTMLDivElement, HelpCircleRootProps>(
|
||||
function HelpCircleRoot(
|
||||
{
|
||||
icon = <FeatherHelpCircle />,
|
||||
size = "medium",
|
||||
shape = "default",
|
||||
tooltipText,
|
||||
className,
|
||||
...otherProps
|
||||
}: HelpCircleRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<SubframeCore.Tooltip.Provider>
|
||||
<SubframeCore.Tooltip.Root>
|
||||
<SubframeCore.Tooltip.Trigger asChild={true}>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/bdf8292f flex cursor-pointer items-center justify-center rounded-md px-1 py-1",
|
||||
{
|
||||
"bg-surface-feedback-subtle-neutral hover:bg-surface-feedback-subtle-neutral-hover":
|
||||
shape === "background",
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{icon ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-default-medium-0 font-body-default-medium-0 text-icon-neutral-tertiary group-hover/bdf8292f:text-icon-neutral-secondary",
|
||||
{
|
||||
"text-body-large-0 font-body-large-0": size === "large",
|
||||
"text-body-small-regular-0 font-body-small-regular-0":
|
||||
size === "small",
|
||||
}
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
</div>
|
||||
</SubframeCore.Tooltip.Trigger>
|
||||
<SubframeCore.Tooltip.Portal>
|
||||
<SubframeCore.Tooltip.Content
|
||||
side="top"
|
||||
align="center"
|
||||
sideOffset={4}
|
||||
asChild={true}
|
||||
>
|
||||
<div className="flex max-w-[384px] flex-col items-start gap-2 rounded-sm bg-surface-feedback-solid-neutral-active px-1.5 py-0.5 shadow-lg">
|
||||
{tooltipText ? (
|
||||
<span className="text-body-extra-small-medium-150 font-body-extra-small-medium-150 text-text-neutral-on-solid">
|
||||
{tooltipText}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</SubframeCore.Tooltip.Content>
|
||||
</SubframeCore.Tooltip.Portal>
|
||||
</SubframeCore.Tooltip.Root>
|
||||
</SubframeCore.Tooltip.Provider>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const HelpCircle = HelpCircleRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { HelpCircle as HelpCircleComponent } from "./HelpCircle";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const HelpCircle = HelpCircleComponent;
|
||||
@@ -0,0 +1,132 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Icon — https://app.subframe.com/f023064a5ca0/library?component=Icon_e24ec5dc-9821-430d-8254-1d72a7d46218
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherCircle } from "@subframe/core";
|
||||
import { FeatherCircleDashed } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface IconFeedbackIconProps
|
||||
extends React.ComponentProps<typeof SubframeCore.IconWrapper> {
|
||||
variant?:
|
||||
| "primary"
|
||||
| "secondary"
|
||||
| "tertiary"
|
||||
| "destructive"
|
||||
| "neutral"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "information";
|
||||
size?: "medium" | "small" | "large";
|
||||
icon?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const IconFeedbackIcon = React.forwardRef<
|
||||
React.ElementRef<typeof SubframeCore.IconWrapper>,
|
||||
IconFeedbackIconProps
|
||||
>(function IconFeedbackIcon(
|
||||
{
|
||||
variant = "primary",
|
||||
size = "medium",
|
||||
icon = <FeatherCircle />,
|
||||
className,
|
||||
...otherProps
|
||||
}: IconFeedbackIconProps,
|
||||
ref
|
||||
) {
|
||||
return icon ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/dc696813 text-body-large-0 font-body-large-0 text-icon-neutral-primary relative",
|
||||
{
|
||||
"text-heading-0 font-heading-0": size === "large",
|
||||
"text-body-default-regular-0 font-body-default-regular-0":
|
||||
size === "small",
|
||||
"text-icon-feedback-information": variant === "information",
|
||||
"text-icon-feedback-warning": variant === "warning",
|
||||
"text-icon-feedback-positive": variant === "success",
|
||||
"text-icon-feedback-negative": variant === "destructive",
|
||||
"text-icon-neutral-tertiary": variant === "tertiary",
|
||||
"text-icon-neutral-secondary": variant === "secondary",
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null;
|
||||
});
|
||||
|
||||
export interface IconRootProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
variant?:
|
||||
| "primary"
|
||||
| "secondary"
|
||||
| "tertiary"
|
||||
| "destructive"
|
||||
| "neutral"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "information";
|
||||
size?: "extra-small" | "small" | "medium" | "large" | "extra-large";
|
||||
icon?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const IconRoot = React.forwardRef<HTMLDivElement, IconRootProps>(
|
||||
function IconRoot(
|
||||
{
|
||||
variant = "primary",
|
||||
size = "medium",
|
||||
icon = <FeatherCircleDashed />,
|
||||
className,
|
||||
...otherProps
|
||||
}: IconRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/e24ec5dc flex flex-col items-center justify-center rounded-sm",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{icon ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-large-0 font-body-large-0 text-icon-neutral-primary",
|
||||
{
|
||||
"text-subtitle-0 font-subtitle-0": size === "extra-large",
|
||||
"text-heading-0 font-heading-0": size === "large",
|
||||
"text-body-default-regular-0 font-body-default-regular-0":
|
||||
size === "small",
|
||||
"text-body-small-regular-0 font-body-small-regular-0":
|
||||
size === "extra-small",
|
||||
"text-icon-feedback-information": variant === "information",
|
||||
"text-icon-feedback-warning": variant === "warning",
|
||||
"text-icon-feedback-positive": variant === "success",
|
||||
"text-icon-feedback-negative": variant === "destructive",
|
||||
"text-icon-neutral-tertiary": variant === "tertiary",
|
||||
"text-icon-neutral-secondary": variant === "secondary",
|
||||
}
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const Icon = Object.assign(IconRoot, {
|
||||
IconFeedbackIcon,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { Icon as IconComponent } from "./Icon";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const Icon = IconComponent;
|
||||
@@ -0,0 +1,124 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Icon Button — https://app.subframe.com/f023064a5ca0/library?component=Icon+Button_af9405b1-8c54-4e01-9786-5aad308224f6
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherPlus } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface IconButtonRootProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
disabled?: boolean;
|
||||
loading?: boolean;
|
||||
variant?:
|
||||
| "primary"
|
||||
| "secondary"
|
||||
| "tertiary"
|
||||
| "ghost"
|
||||
| "neutral"
|
||||
| "destructive"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "information";
|
||||
size?: "large" | "medium" | "small" | "extra-small" | "2-x-small";
|
||||
square?: boolean;
|
||||
icon?: React.ReactNode;
|
||||
onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const IconButtonRoot = React.forwardRef<HTMLButtonElement, IconButtonRootProps>(
|
||||
function IconButtonRoot(
|
||||
{
|
||||
disabled = false,
|
||||
loading = false,
|
||||
variant = "primary",
|
||||
size = "medium",
|
||||
square = false,
|
||||
icon = <FeatherPlus />,
|
||||
className,
|
||||
type = "button",
|
||||
...otherProps
|
||||
}: IconButtonRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<button
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/af9405b1 flex h-9 w-9 cursor-pointer items-center justify-center gap-2 rounded-round border-none bg-brand-brand-surface-solid-default text-left hover:bg-brand-brand-surface-solid-hover active:bg-brand-brand-surface-solid-active focus-within:shadow-focus disabled:cursor-default disabled:bg-surface-neutral-disabled disabled:pointer-events-none hover:disabled:cursor-default hover:disabled:bg-surface-neutral-disabled active:disabled:cursor-default active:disabled:bg-surface-neutral-disabled",
|
||||
{
|
||||
"rounded-md": square,
|
||||
"h-4 w-4": size === "2-x-small",
|
||||
"h-6 w-6": size === "extra-small",
|
||||
"h-8 w-8": size === "small",
|
||||
"h-12 w-12": size === "large",
|
||||
"bg-surface-feedback-solid-information hover:bg-surface-feedback-solid-information-hover active:bg-surface-feedback-solid-information-active":
|
||||
variant === "information",
|
||||
"bg-surface-feedback-solid-warning hover:bg-surface-feedback-solid-warning-hover active:bg-surface-feedback-solid-warning-active":
|
||||
variant === "warning",
|
||||
"bg-surface-feedback-solid-positive-default hover:bg-surface-feedback-solid-positive-hover active:bg-surface-feedback-solid-positive-active":
|
||||
variant === "success",
|
||||
"bg-surface-feedback-solid-negative-default hover:bg-surface-feedback-solid-negative-hover active:bg-surface-feedback-solid-negative-active":
|
||||
variant === "destructive",
|
||||
"bg-surface-feedback-solid-neutral hover:bg-surface-feedback-solid-neutral-hover active:bg-surface-feedback-solid-neutral-active":
|
||||
variant === "neutral",
|
||||
"bg-transparent hover:bg-surface-neutral-tertiary active:bg-surface-neutral-tertiary":
|
||||
variant === "ghost",
|
||||
"border border-solid border-border-default bg-surface-neutral-primary hover:border hover:border-solid hover:border-border-strong hover:bg-surface-neutral-secondary active:bg-surface-neutral-tertiary":
|
||||
variant === "tertiary",
|
||||
"bg-brand-brand-secondary-surface-subtle-default hover:bg-brand-brand-secondary-surface-subtle-hover active:bg-brand-brand-secondary-surface-subtle-active":
|
||||
variant === "secondary",
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
type={type}
|
||||
disabled={disabled}
|
||||
{...otherProps}
|
||||
>
|
||||
{icon ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-large-0 font-body-large-0 text-icon-neutral-on-solid group-disabled/af9405b1:text-icon-neutral-disabled",
|
||||
{
|
||||
"text-body-small-semibold-0 font-body-small-semibold-0":
|
||||
size === "2-x-small",
|
||||
"text-body-default-semibold-0 font-body-default-semibold-0":
|
||||
size === "extra-small",
|
||||
"text-heading-0 font-heading-0": size === "large",
|
||||
"text-icon-neutral-secondary": variant === "ghost",
|
||||
"text-icon-neutral-on-subtle": variant === "tertiary",
|
||||
"text-icon-neutral-primary": variant === "secondary",
|
||||
hidden: loading,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden h-4 w-4 flex-none items-center justify-center gap-2",
|
||||
{ "h-5 w-5": size === "large", flex: loading }
|
||||
)}
|
||||
>
|
||||
<SubframeCore.Loader
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-extra-small-regular-150 font-body-extra-small-regular-150 text-icon-neutral-on-solid group-disabled/af9405b1:text-icon-neutral-disabled group-hover/af9405b1:group-disabled/af9405b1:text-icon-neutral-on-solid group-active/af9405b1:group-disabled/af9405b1:text-icon-neutral-disabled",
|
||||
{
|
||||
"text-icon-neutral-secondary": variant === "ghost",
|
||||
"text-icon-neutral-on-subtle": variant === "tertiary",
|
||||
"text-icon-neutral-primary": variant === "secondary",
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const IconButton = IconButtonRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { IconButton as IconButtonComponent } from "./IconButton";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const IconButton = IconButtonComponent;
|
||||
@@ -0,0 +1,160 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Icon with Background — https://app.subframe.com/f023064a5ca0/library?component=Icon+with+Background_4a18e9fc-cea3-46ca-8c53-2412462342ad
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherFlag } from "@subframe/core";
|
||||
import { FeatherMail } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface IconFeedbackIconProps
|
||||
extends React.ComponentProps<typeof SubframeCore.IconWrapper> {
|
||||
variant?:
|
||||
| "primary"
|
||||
| "secondary"
|
||||
| "tertiary"
|
||||
| "destructive"
|
||||
| "neutral"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "information";
|
||||
size?: "medium" | "small" | "large";
|
||||
icon?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const IconFeedbackIcon = React.forwardRef<
|
||||
React.ElementRef<typeof SubframeCore.IconWrapper>,
|
||||
IconFeedbackIconProps
|
||||
>(function IconFeedbackIcon(
|
||||
{
|
||||
variant = "primary",
|
||||
size = "medium",
|
||||
icon = <FeatherFlag />,
|
||||
className,
|
||||
...otherProps
|
||||
}: IconFeedbackIconProps,
|
||||
ref
|
||||
) {
|
||||
return icon ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/8e5c09be text-body-large-0 font-body-large-0 text-icon-neutral-primary relative",
|
||||
{
|
||||
"text-heading-0 font-heading-0": size === "large",
|
||||
"text-body-default-regular-0 font-body-default-regular-0":
|
||||
size === "small",
|
||||
"text-icon-feedback-information": variant === "information",
|
||||
"text-icon-feedback-warning": variant === "warning",
|
||||
"text-icon-feedback-positive": variant === "success",
|
||||
"text-icon-feedback-negative": variant === "destructive",
|
||||
"text-icon-neutral-tertiary": variant === "tertiary",
|
||||
"text-icon-neutral-secondary": variant === "secondary",
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null;
|
||||
});
|
||||
|
||||
export interface IconWithBackgroundRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
variant?:
|
||||
| "primary"
|
||||
| "secondary"
|
||||
| "tertiary"
|
||||
| "destructive"
|
||||
| "neutral"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "information";
|
||||
size?:
|
||||
| "extra-small"
|
||||
| "small"
|
||||
| "medium"
|
||||
| "large"
|
||||
| "extra-large"
|
||||
| "2-x-large"
|
||||
| "3-x-large";
|
||||
round?: boolean;
|
||||
icon?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const IconWithBackgroundRoot = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
IconWithBackgroundRootProps
|
||||
>(function IconWithBackgroundRoot(
|
||||
{
|
||||
variant = "primary",
|
||||
size = "medium",
|
||||
round = false,
|
||||
icon = <FeatherMail />,
|
||||
className,
|
||||
...otherProps
|
||||
}: IconWithBackgroundRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/4a18e9fc flex flex-col items-center justify-center gap-2 rounded-sm bg-brand-brand-surface-subtle-default px-1.5 py-1.5",
|
||||
{
|
||||
"rounded-round": round,
|
||||
"px-4 py-4":
|
||||
size === "3-x-large" ||
|
||||
size === "2-x-large" ||
|
||||
size === "extra-large",
|
||||
"px-2.5 py-2.5": size === "large",
|
||||
"bg-surface-feedback-subtle-information": variant === "information",
|
||||
"bg-surface-feedback-subtle-warning": variant === "warning",
|
||||
"bg-surface-feedback-subtle-positive": variant === "success",
|
||||
"bg-surface-feedback-subtle-neutral": variant === "neutral",
|
||||
"bg-surface-feedback-subtle-negative": variant === "destructive",
|
||||
"bg-surface-neutral-tertiary": variant === "tertiary",
|
||||
"bg-surface-neutral-secondary": variant === "secondary",
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{icon ? (
|
||||
<SubframeCore.IconWrapper
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-large-0 font-body-large-0 text-icon-neutral-primary relative",
|
||||
{
|
||||
"text-title-hero font-title-hero": size === "3-x-large",
|
||||
"text-title-page font-title-page": size === "2-x-large",
|
||||
"text-subtitle-0 font-subtitle-0": size === "extra-large",
|
||||
"text-heading-0 font-heading-0": size === "large",
|
||||
"text-body-default-regular-0 font-body-default-regular-0":
|
||||
size === "small",
|
||||
"text-body-small-semibold-0 font-body-small-semibold-0":
|
||||
size === "extra-small",
|
||||
"text-icon-feedback-information": variant === "information",
|
||||
"text-icon-feedback-warning": variant === "warning",
|
||||
"text-icon-feedback-positive": variant === "success",
|
||||
"text-icon-feedback-negative": variant === "destructive",
|
||||
"text-icon-neutral-tertiary": variant === "tertiary",
|
||||
"text-icon-neutral-secondary": variant === "secondary",
|
||||
}
|
||||
)}
|
||||
>
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const IconWithBackground = Object.assign(IconWithBackgroundRoot, {
|
||||
IconFeedbackIcon,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { IconWithBackground as IconWithBackgroundComponent } from "./IconWithBackground";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const IconWithBackground = IconWithBackgroundComponent;
|
||||
@@ -0,0 +1,139 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Input OTP — https://app.subframe.com/f023064a5ca0/library?component=Input+OTP_1a223fbe-dca0-4e44-a383-44bbc7db975d
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface InputProps
|
||||
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "placeholder"> {
|
||||
placeholder?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const Input = React.forwardRef<HTMLInputElement, InputProps>(function Input(
|
||||
{ placeholder, className, ...otherProps }: InputProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<input
|
||||
className={SubframeUtils.twClassNames(
|
||||
"h-full w-full text-body-large-0 font-body-large-0 text-text-neutral-primary outline-none placeholder:text-text-neutral-subtle text-center",
|
||||
className
|
||||
)}
|
||||
placeholder={placeholder as string}
|
||||
value=""
|
||||
ref={ref}
|
||||
type="text"
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export interface OtpInputItemProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
success?: boolean;
|
||||
error?: boolean;
|
||||
disabled?: boolean;
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const OtpInputItem = React.forwardRef<HTMLDivElement, OtpInputItemProps>(
|
||||
function OtpInputItem(
|
||||
{
|
||||
success = false,
|
||||
error = false,
|
||||
disabled = false,
|
||||
children,
|
||||
className,
|
||||
...otherProps
|
||||
}: OtpInputItemProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/7a2f2337 flex h-14 w-12 cursor-pointer items-center justify-center gap-2 rounded-md border border-solid border-border-default bg-surface-neutral-primary px-2 py-2 hover:border hover:border-solid hover:border-border-strong focus-within:shadow-focus",
|
||||
{
|
||||
"border-none bg-surface-neutral-disabled hover:border-none focus-within:shadow-none":
|
||||
disabled,
|
||||
"border border-solid border-border-negative-solid hover:border hover:border-solid hover:border-border-negative-solid":
|
||||
error,
|
||||
"border-none bg-surface-feedback-subtle-positive hover:border-none":
|
||||
success,
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{children ? (
|
||||
<div className="flex grow shrink-0 basis-0 items-center justify-center self-stretch">
|
||||
{children}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export interface InputOtpRootProps
|
||||
extends Omit<React.HTMLAttributes<HTMLDivElement>, "title"> {
|
||||
state?: "default" | "error" | "disabled";
|
||||
title?: React.ReactNode;
|
||||
helperText?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const InputOtpRoot = React.forwardRef<HTMLDivElement, InputOtpRootProps>(
|
||||
function InputOtpRoot(
|
||||
{
|
||||
state = "default",
|
||||
title,
|
||||
helperText,
|
||||
children,
|
||||
className,
|
||||
...otherProps
|
||||
}: InputOtpRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/1a223fbe flex flex-col items-center gap-4",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
{title ? (
|
||||
<span className="text-body-large-0 font-body-large-0 text-text-neutral-primary">
|
||||
{title}
|
||||
</span>
|
||||
) : null}
|
||||
{children ? (
|
||||
<div className="flex items-center gap-2">{children}</div>
|
||||
) : null}
|
||||
{helperText ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"text-body-small-regular-0 font-body-small-regular-0 text-text-neutral-secondary",
|
||||
{ "text-text-feedback-negative": state === "error" }
|
||||
)}
|
||||
>
|
||||
{helperText}
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const InputOtp = Object.assign(InputOtpRoot, {
|
||||
Input,
|
||||
OtpInputItem,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { InputOtp as InputOtpComponent } from "./InputOtp";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const InputOtp = InputOtpComponent;
|
||||
@@ -0,0 +1,143 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Icon — https://app.subframe.com/f023064a5ca0/library?component=Icon_e24ec5dc-9821-430d-8254-1d72a7d46218
|
||||
* KanbanCard — https://app.subframe.com/f023064a5ca0/library?component=KanbanCard_27235bcb-e61b-4880-9ae6-3f12d290a434
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherFlag } from "@subframe/core";
|
||||
import { FeatherLock } from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
import { Icon } from "../Icon";
|
||||
|
||||
export interface KanbanCardRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
tagsSlot?: React.ReactNode;
|
||||
hideTags?: boolean;
|
||||
titleSlot?: React.ReactNode;
|
||||
valueSlot?: React.ReactNode;
|
||||
priceLocked?: boolean;
|
||||
footerLeftSlot?: React.ReactNode;
|
||||
footerRightSlot?: React.ReactNode;
|
||||
flagVariant?:
|
||||
| "primary"
|
||||
| "secondary"
|
||||
| "tertiary"
|
||||
| "destructive"
|
||||
| "neutral"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "information";
|
||||
keepTagRowSpace?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const KanbanCardRoot = React.forwardRef<HTMLDivElement, KanbanCardRootProps>(
|
||||
function KanbanCardRoot(
|
||||
{
|
||||
tagsSlot,
|
||||
hideTags = false,
|
||||
titleSlot,
|
||||
valueSlot,
|
||||
priceLocked = false,
|
||||
footerLeftSlot,
|
||||
footerRightSlot,
|
||||
flagVariant = "primary",
|
||||
keepTagRowSpace = false,
|
||||
className,
|
||||
...otherProps
|
||||
}: KanbanCardRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/27235bcb flex w-full min-w-[0px] flex-col items-start gap-3 max-w-full",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className="flex w-full min-w-[0px] flex-col items-start gap-4 rounded-lg border border-solid border-border-default bg-surface-neutral-secondary px-3 py-3 shadow-sm relative max-w-full">
|
||||
<Icon
|
||||
className="absolute top-3 right-3"
|
||||
variant={
|
||||
flagVariant === "information"
|
||||
? "information"
|
||||
: flagVariant === "warning"
|
||||
? "warning"
|
||||
: flagVariant === "success"
|
||||
? "success"
|
||||
: flagVariant === "neutral"
|
||||
? "neutral"
|
||||
: flagVariant === "destructive"
|
||||
? "destructive"
|
||||
: flagVariant === "tertiary"
|
||||
? "tertiary"
|
||||
: flagVariant === "secondary"
|
||||
? "secondary"
|
||||
: "primary"
|
||||
}
|
||||
icon={<FeatherFlag />}
|
||||
/>
|
||||
{tagsSlot ? (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full min-w-[0px] flex-wrap items-center gap-1.5 pr-7 grow",
|
||||
{ hidden: hideTags }
|
||||
)}
|
||||
>
|
||||
{tagsSlot}
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden h-5 w-full min-w-[0px] flex-none flex-wrap items-center gap-1.5 pr-7",
|
||||
{ flex: keepTagRowSpace }
|
||||
)}
|
||||
/>
|
||||
{titleSlot ? (
|
||||
<div className="flex w-full min-w-[0px] flex-col items-start justify-center gap-2 pr-7">
|
||||
{titleSlot}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex w-full min-w-[0px] flex-wrap items-center gap-2">
|
||||
{valueSlot ? (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex min-w-[0px] flex-col items-start gap-2",
|
||||
{ hidden: priceLocked }
|
||||
)}
|
||||
>
|
||||
{valueSlot}
|
||||
</div>
|
||||
) : null}
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden min-w-[0px] items-center gap-1.5",
|
||||
{ flex: priceLocked }
|
||||
)}
|
||||
>
|
||||
<FeatherLock className="text-body-small-medium-0 font-body-small-medium-0 text-icon-neutral-tertiary" />
|
||||
<span className="break-words text-body-small-regular-0 font-body-small-regular-0 text-text-neutral-tertiary">
|
||||
Umsatz gesperrt
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex h-px w-full flex-none flex-col items-center gap-2 bg-border-default" />
|
||||
<div className="flex w-full min-w-[0px] flex-wrap items-center justify-between gap-x-2 gap-y-2">
|
||||
{footerLeftSlot ? (
|
||||
<div className="flex items-center">{footerLeftSlot}</div>
|
||||
) : null}
|
||||
{footerRightSlot ? (
|
||||
<div className="flex items-center">{footerRightSlot}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export const KanbanCard = KanbanCardRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { KanbanCard as KanbanCardComponent } from "./KanbanCard";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const KanbanCard = KanbanCardComponent;
|
||||
@@ -0,0 +1,171 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* KanbanColumn — https://app.subframe.com/f023064a5ca0/library?component=KanbanColumn_bbc08458-14aa-4512-a807-e673a64315ab
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { FeatherChevronsLeft } from "@subframe/core";
|
||||
import { FeatherChevronsRight } from "@subframe/core";
|
||||
import { FeatherLock } from "@subframe/core";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface KanbanColumnRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
collapsed?: boolean;
|
||||
name?: React.ReactNode;
|
||||
price?: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
badge?: React.ReactNode;
|
||||
cards?: React.ReactNode;
|
||||
sumLocked?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const KanbanColumnRoot = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
KanbanColumnRootProps
|
||||
>(function KanbanColumnRoot(
|
||||
{
|
||||
collapsed = false,
|
||||
name,
|
||||
price,
|
||||
icon = <FeatherChevronsLeft />,
|
||||
badge,
|
||||
cards,
|
||||
sumLocked = false,
|
||||
className,
|
||||
...otherProps
|
||||
}: KanbanColumnRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/bbc08458 flex w-80 flex-col items-center gap-2 rounded-md border-2 border-solid border-border-default bg-surface-neutral-tertiary px-1.5 py-1.5 shrink-0 min-w-80 self-start",
|
||||
{ "h-full w-12 px-0 min-w-12": collapsed },
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full flex-col items-center",
|
||||
{ "grow shrink-0 basis-0 bg-surface-neutral-tertiary": collapsed }
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full items-center justify-between px-1 pt-1.5 pb-1 gap-2",
|
||||
{ "flex-col rounded-md py-3 cursor-pointer": collapsed }
|
||||
)}
|
||||
>
|
||||
<FeatherChevronsRight
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden text-body-small-semibold-0 font-body-small-semibold-0 text-icon-neutral-tertiary",
|
||||
{ "inline-flex": collapsed }
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden grow shrink-0 basis-0 flex-col items-center gap-2 self-stretch py-3",
|
||||
{ flex: collapsed }
|
||||
)}
|
||||
>
|
||||
<div className="flex h-0.5 w-full flex-none flex-col items-center gap-2 bg-border-default" />
|
||||
</div>
|
||||
{name ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden break-words text-body-small-semibold-150 font-body-small-semibold-150 text-text-neutral-primary [writing-mode:vertical-rl] rotate-180",
|
||||
{ inline: collapsed }
|
||||
)}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
) : null}
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex min-w-[0px] items-center gap-2.5 grow shrink",
|
||||
{ "flex-col px-1 py-1": collapsed }
|
||||
)}
|
||||
>
|
||||
{name ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"min-w-[0px] break-words text-body-small-semibold-150 font-body-small-semibold-150 text-text-neutral-primary",
|
||||
{ hidden: collapsed }
|
||||
)}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
) : null}
|
||||
{badge ? (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex items-center gap-2.5 shrink-0",
|
||||
{ "px-1 py-2": collapsed }
|
||||
)}
|
||||
>
|
||||
{badge}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex min-w-[0px] items-center gap-2 shrink-0",
|
||||
{ hidden: collapsed }
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden items-center gap-1 shrink-0",
|
||||
{ flex: sumLocked }
|
||||
)}
|
||||
>
|
||||
<FeatherLock className="text-body-extra-small-regular-0 font-body-extra-small-regular-0 text-icon-neutral-subtle" />
|
||||
<span className="whitespace-nowrap text-body-extra-small-regular-150 font-body-extra-small-regular-150 text-text-neutral-subtle">
|
||||
Summe gesperrt
|
||||
</span>
|
||||
</div>
|
||||
{price ? (
|
||||
<span
|
||||
className={SubframeUtils.twClassNames(
|
||||
"break-words text-body-small-regular-0 font-body-small-regular-0 text-text-neutral-tertiary",
|
||||
{ hidden: sumLocked }
|
||||
)}
|
||||
>
|
||||
{price}
|
||||
</span>
|
||||
) : null}
|
||||
{icon ? (
|
||||
<SubframeCore.IconWrapper className="text-body-small-semibold-0 font-body-small-semibold-0 text-icon-neutral-tertiary">
|
||||
{icon}
|
||||
</SubframeCore.IconWrapper>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex h-0.5 w-full flex-none flex-col items-center gap-2 bg-border-default",
|
||||
{ hidden: collapsed }
|
||||
)}
|
||||
/>
|
||||
{cards ? (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full flex-col items-center gap-3 pb-2",
|
||||
{ hidden: collapsed }
|
||||
)}
|
||||
>
|
||||
{cards}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const KanbanColumn = KanbanColumnRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { KanbanColumn as KanbanColumnComponent } from "./KanbanColumn";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const KanbanColumn = KanbanColumnComponent;
|
||||
@@ -0,0 +1,49 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Kanban Subbar — https://app.subframe.com/f023064a5ca0/library?component=Kanban+Subbar_75a855a6-3419-4c84-97bd-540dd2f180a4
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface KanbanSubbarRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
header?: React.ReactNode;
|
||||
badge?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const KanbanSubbarRoot = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
KanbanSubbarRootProps
|
||||
>(function KanbanSubbarRoot(
|
||||
{ header, badge, children, className, ...otherProps }: KanbanSubbarRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex w-full flex-col items-start gap-4 bg-surface-neutral-secondary px-4 py-4",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div className="flex items-center gap-4 px-2 py-1">
|
||||
{header ? (
|
||||
<span className="text-body-large-0 font-body-large-0 text-text-neutral-primary">
|
||||
{header}
|
||||
</span>
|
||||
) : null}
|
||||
{badge ? <div className="flex items-center gap-4">{badge}</div> : null}
|
||||
</div>
|
||||
{children ? (
|
||||
<div className="flex w-full flex-col items-start gap-2">{children}</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const KanbanSubbar = KanbanSubbarRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { KanbanSubbar as KanbanSubbarComponent } from "./KanbanSubbar";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const KanbanSubbar = KanbanSubbarComponent;
|
||||
@@ -0,0 +1,40 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Line Chart — https://app.subframe.com/f023064a5ca0/library?component=Line+Chart_22944dd2-3cdd-42fd-913a-1b11a3c1d16d
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface LineChartRootProps
|
||||
extends React.ComponentProps<typeof SubframeCore.LineChart> {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const LineChartRoot = React.forwardRef<
|
||||
React.ElementRef<typeof SubframeCore.LineChart>,
|
||||
LineChartRootProps
|
||||
>(function LineChartRoot(
|
||||
{ className, ...otherProps }: LineChartRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<SubframeCore.LineChart
|
||||
className={SubframeUtils.twClassNames("h-80 w-full", className)}
|
||||
ref={ref}
|
||||
colors={[
|
||||
"#3b82f6",
|
||||
"#bfdbfe",
|
||||
"#2563eb",
|
||||
"#93c5fd",
|
||||
"#1d4ed8",
|
||||
"#60a5fa",
|
||||
]}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const LineChart = LineChartRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { LineChart as LineChartComponent } from "./LineChart";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const LineChart = LineChartComponent;
|
||||
@@ -0,0 +1,107 @@
|
||||
"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;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { LinkButton as LinkButtonComponent } from "./LinkButton";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const LinkButton = LinkButtonComponent;
|
||||
@@ -0,0 +1,41 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Loader — https://app.subframe.com/f023064a5ca0/library?component=Loader_f2e570c8-e463-45c2-aae9-a960146bc5d5
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeCore from "@subframe/core";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface LoaderRootProps
|
||||
extends React.ComponentProps<typeof SubframeCore.Loader> {
|
||||
size?: "small" | "medium" | "large";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const LoaderRoot = React.forwardRef<
|
||||
React.ElementRef<typeof SubframeCore.Loader>,
|
||||
LoaderRootProps
|
||||
>(function LoaderRoot(
|
||||
{ size = "medium", className, ...otherProps }: LoaderRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<SubframeCore.Loader
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/f2e570c8 font-['Inter'] text-[14px] font-[400] leading-[20px] text-[#2563eb]",
|
||||
{
|
||||
"text-[20px] font-[600] leading-[24px] tracking-normal":
|
||||
size === "large",
|
||||
"text-[12px] leading-[16px] tracking-normal": size === "small",
|
||||
},
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
export const Loader = LoaderRoot;
|
||||
@@ -0,0 +1,10 @@
|
||||
"use client";
|
||||
import { Loader as LoaderComponent } from "./Loader";
|
||||
|
||||
/**
|
||||
* Add wrapper components and business logic here.
|
||||
* If you modify this file, disable Subframe sync for it to prevent overwrites.
|
||||
* Learn more: https://docs.subframe.com/concepts/syncing-components#wrapping-components
|
||||
*/
|
||||
|
||||
export const Loader = LoaderComponent;
|
||||
@@ -0,0 +1,79 @@
|
||||
"use client";
|
||||
/*
|
||||
* Documentation:
|
||||
* Loading Indicator Skeleton — https://app.subframe.com/f023064a5ca0/library?component=Loading+Indicator+Skeleton_08dee637-2ba5-4c5d-86d4-a531fd11481f
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import * as SubframeUtils from "../../utils";
|
||||
|
||||
export interface LoadingIndicatorSkeletonRootProps
|
||||
extends React.HTMLAttributes<HTMLDivElement> {
|
||||
variant?: "card" | "list";
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const LoadingIndicatorSkeletonRoot = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
LoadingIndicatorSkeletonRootProps
|
||||
>(function LoadingIndicatorSkeletonRoot(
|
||||
{
|
||||
variant = "card",
|
||||
className,
|
||||
...otherProps
|
||||
}: LoadingIndicatorSkeletonRootProps,
|
||||
ref
|
||||
) {
|
||||
return (
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"group/08dee637 flex w-full items-start rounded-[8px] border border-solid border-[#e7e7e7] bg-surface-neutral-primary px-6 py-6 group/mtskeleton",
|
||||
{ "border border-solid border-border-default": variant === "list" },
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...otherProps}
|
||||
>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"flex grow shrink-0 basis-0 flex-col items-start gap-4",
|
||||
{ hidden: variant === "list" }
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex h-10 w-10 flex-none items-start rounded-[9999px] bg-surface-neutral-tertiary animate-pulse" />
|
||||
<div className="flex min-w-[0px] flex-col items-start gap-2 flex-1">
|
||||
<div className="flex h-3 flex-none items-start rounded-[4px] bg-[#e7e7e7] w-1/2 animate-pulse" />
|
||||
<div className="flex h-3 flex-none items-start rounded-[4px] bg-[#e7e7e7] w-1/3 animate-pulse" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex h-32 w-full flex-none items-start rounded-[4px] bg-surface-neutral-tertiary animate-pulse" />
|
||||
<div className="flex h-3 flex-none items-start rounded-[4px] bg-surface-neutral-tertiary w-1/3 animate-pulse" />
|
||||
</div>
|
||||
<div
|
||||
className={SubframeUtils.twClassNames(
|
||||
"hidden min-w-[0px] grow shrink-0 basis-0 flex-col items-start",
|
||||
{ flex: variant === "list" }
|
||||
)}
|
||||
>
|
||||
<div className="flex w-full items-start gap-3 pb-4">
|
||||
<div className="flex h-8 w-8 flex-none items-start rounded-[4px] bg-surface-neutral-tertiary animate-pulse" />
|
||||
<div className="flex min-w-[0px] flex-col items-start gap-2 flex-1">
|
||||
<div className="flex h-20 w-full flex-none items-start rounded-[4px] bg-surface-neutral-tertiary animate-pulse" />
|
||||
<div className="flex h-3 flex-none items-start rounded-[4px] bg-surface-neutral-tertiary w-1/2 animate-pulse" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-full items-center gap-3 border-t border-solid border-[#e7e7e7] py-4">
|
||||
<div className="flex h-8 w-8 flex-none items-start rounded-[4px] bg-surface-neutral-tertiary animate-pulse" />
|
||||
<div className="flex h-3 min-w-[0px] items-start rounded-[4px] bg-surface-neutral-tertiary flex-1 animate-pulse" />
|
||||
</div>
|
||||
<div className="flex w-full items-center gap-3 border-t border-solid border-[#e7e7e7] py-4">
|
||||
<div className="flex h-8 w-8 flex-none items-start rounded-[4px] bg-surface-neutral-disabled animate-pulse" />
|
||||
<div className="flex h-3 min-w-[0px] items-start rounded-[4px] bg-surface-neutral-tertiary flex-1 animate-pulse" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const LoadingIndicatorSkeleton = LoadingIndicatorSkeletonRoot;
|
||||
Loaded 100 of 250 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user