@usememos/ui 26.11.0 → 26.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import{X as
|
|
2
|
+
import{X as L}from"lucide-react";import{forwardRef as s}from"react";import{cx as n,withClass as l}from"../../internal/class-name.js";import{SizeContext as b}from"../../internal/size.js";import{Button as v}from"../button/index.js";import{jsx as t,jsxs as u}from"react/jsx-runtime";function w(i,p,g){let c=s(function({className:o,...e},r){return t(i.Trigger,{ref:r,...e,className:l(`mu-${p}-trigger`,o)})}),m=s(function({container:o,sheet:e=!0,closeLabel:r="Close",className:T,children:x,...C},H){return u(i.Portal,{container:o,children:[t(i.Backdrop,{className:"mu-dialog-backdrop"}),t(i.Viewport,{className:n("mu-dialog-viewport",e&&"mu-dialog-sheet"),children:u(i.Popup,{ref:H,...C,className:l(`mu-dialog-popup mu-${p}-popup mu-popup mu-base mu-s-md`,T),children:[t(b.Provider,{value:"md",children:x}),g?t(i.Close,{className:"mu-dialog-x",render:t(v,{variant:"ghost",size:"sm",icon:t(L,{}),"aria-label":r})}):null]})})]})}),P=s(function({className:o,...e},r){return t(i.Title,{ref:r,...e,className:l("mu-dialog-title mu-title",o)})}),f=s(function({className:o,...e},r){return t(i.Description,{ref:r,...e,className:l("mu-dialog-description mu-body",o)})}),d=s(function({className:o,...e},r){return t(i.Close,{ref:r,...e,className:l(`mu-${p}-close`,o)})}),D=s(function({className:o,...e},r){return t("div",{ref:r,...e,className:n("mu-dialog-footer",o)})});return{Root:i.Root,Trigger:c,Popup:m,Title:P,Description:f,Close:d,Footer:D}}export{w as createDialogParts};
|
|
3
3
|
//# sourceMappingURL=parts.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/dialog/parts.tsx"],
|
|
4
|
-
"sourcesContent": ["import { Dialog as BaseDialog } from '@base-ui/react/dialog';\nimport { X } from 'lucide-react';\nimport { forwardRef, type HTMLAttributes } from 'react';\nimport { cx, withClass } from '../../internal/class-name';\nimport { Button } from '../button';\n\nexport type RootProps = BaseDialog.Root.Props;\nexport interface TriggerProps extends BaseDialog.Trigger.Props {}\nexport interface PopupProps extends BaseDialog.Popup.Props {\n /** Where the dialog is portaled to. Default `document.body`. */\n container?: BaseDialog.Portal.Props['container'];\n /** Below `breakpoint-sm` the dialog becomes a sheet from the bottom. `false` keeps it centered. Default true. */\n sheet?: boolean;\n}\nexport interface TitleProps extends BaseDialog.Title.Props {}\nexport interface DescriptionProps extends BaseDialog.Description.Props {}\nexport interface CloseProps extends BaseDialog.Close.Props {}\nexport interface FooterProps extends HTMLAttributes<HTMLDivElement> {}\n\n/**\n * Builds the styled parts on Base UI's Dialog or AlertDialog, which share\n * their anatomy. `closeButton` adds the X at the top right of the popup.\n */\nexport function createDialogParts(Base: typeof BaseDialog, name: 'dialog' | 'alert-dialog', closeButton: boolean) {\n const Trigger = forwardRef<HTMLButtonElement, TriggerProps>(function DialogTrigger({ className, ...props }, ref) {\n return <Base.Trigger ref={ref} {...props} className={withClass(`mu-${name}-trigger`, className)} />;\n });\n\n const Popup = forwardRef<HTMLDivElement, PopupProps & { closeLabel?: string }>(function DialogPopup(\n { container, sheet = true, closeLabel = 'Close', className, children, ...props },\n ref,\n ) {\n return (\n <Base.Portal container={container}>\n <Base.Backdrop className=\"mu-dialog-backdrop\" />\n <Base.Viewport className={cx('mu-dialog-viewport', sheet && 'mu-dialog-sheet')}>\n <Base.Popup ref={ref} {...props} className={withClass(`mu-dialog-popup mu-${name}-popup mu-popup mu-base mu-s-md`, className)}>\n {
|
|
5
|
-
"mappings": ";AACA,OAAS,KAAAA,MAAS,eAClB,OAAS,cAAAC,MAAuC,QAChD,OAAS,MAAAC,EAAI,aAAAC,MAAiB,+BAC9B,OAAS,UAAAC,MAAc,qBAqBZ,cAAAC,EAWD,QAAAC,MAXC,oBAFJ,SAASC,EAAkBC,EAAyBC,EAAiCC,EAAsB,CAChH,IAAMC,
|
|
6
|
-
"names": ["X", "forwardRef", "cx", "withClass", "Button", "jsx", "jsxs", "createDialogParts", "Base", "name", "closeButton", "Trigger", "className", "props", "ref", "Popup", "container", "sheet", "closeLabel", "children", "Title", "Description", "Close", "Footer"]
|
|
4
|
+
"sourcesContent": ["import { Dialog as BaseDialog } from '@base-ui/react/dialog';\nimport { X } from 'lucide-react';\nimport { forwardRef, type HTMLAttributes } from 'react';\nimport { cx, withClass } from '../../internal/class-name';\nimport { SizeContext } from '../../internal/size';\nimport { Button } from '../button';\n\nexport type RootProps = BaseDialog.Root.Props;\nexport interface TriggerProps extends BaseDialog.Trigger.Props {}\nexport interface PopupProps extends BaseDialog.Popup.Props {\n /** Where the dialog is portaled to. Default `document.body`. */\n container?: BaseDialog.Portal.Props['container'];\n /** Below `breakpoint-sm` the dialog becomes a sheet from the bottom. `false` keeps it centered. Default true. */\n sheet?: boolean;\n}\nexport interface TitleProps extends BaseDialog.Title.Props {}\nexport interface DescriptionProps extends BaseDialog.Description.Props {}\nexport interface CloseProps extends BaseDialog.Close.Props {}\nexport interface FooterProps extends HTMLAttributes<HTMLDivElement> {}\n\n/**\n * Builds the styled parts on Base UI's Dialog or AlertDialog, which share\n * their anatomy. `closeButton` adds the X at the top right of the popup.\n */\nexport function createDialogParts(Base: typeof BaseDialog, name: 'dialog' | 'alert-dialog', closeButton: boolean) {\n const Trigger = forwardRef<HTMLButtonElement, TriggerProps>(function DialogTrigger({ className, ...props }, ref) {\n return <Base.Trigger ref={ref} {...props} className={withClass(`mu-${name}-trigger`, className)} />;\n });\n\n const Popup = forwardRef<HTMLDivElement, PopupProps & { closeLabel?: string }>(function DialogPopup(\n { container, sheet = true, closeLabel = 'Close', className, children, ...props },\n ref,\n ) {\n return (\n <Base.Portal container={container}>\n <Base.Backdrop className=\"mu-dialog-backdrop\" />\n <Base.Viewport className={cx('mu-dialog-viewport', sheet && 'mu-dialog-sheet')}>\n <Base.Popup ref={ref} {...props} className={withClass(`mu-dialog-popup mu-${name}-popup mu-popup mu-base mu-s-md`, className)}>\n {/* A dialog is md wherever it is declared: React context crosses the portal, so a size set by a parent Root (a sm Table) would leak in. */}\n <SizeContext.Provider value=\"md\">{children}</SizeContext.Provider>\n {closeButton ? (\n <Base.Close className=\"mu-dialog-x\" render={<Button variant=\"ghost\" size=\"sm\" icon={<X />} aria-label={closeLabel} />} />\n ) : null}\n </Base.Popup>\n </Base.Viewport>\n </Base.Portal>\n );\n });\n\n const Title = forwardRef<HTMLHeadingElement, TitleProps>(function DialogTitle({ className, ...props }, ref) {\n return <Base.Title ref={ref} {...props} className={withClass('mu-dialog-title mu-title', className)} />;\n });\n\n const Description = forwardRef<HTMLParagraphElement, DescriptionProps>(function DialogDescription({ className, ...props }, ref) {\n return <Base.Description ref={ref} {...props} className={withClass('mu-dialog-description mu-body', className)} />;\n });\n\n const Close = forwardRef<HTMLButtonElement, CloseProps>(function DialogClose({ className, ...props }, ref) {\n return <Base.Close ref={ref} {...props} className={withClass(`mu-${name}-close`, className)} />;\n });\n\n const Footer = forwardRef<HTMLDivElement, FooterProps>(function DialogFooter({ className, ...props }, ref) {\n return <div ref={ref} {...props} className={cx('mu-dialog-footer', className)} />;\n });\n\n return { Root: Base.Root, Trigger, Popup, Title, Description, Close, Footer };\n}\n"],
|
|
5
|
+
"mappings": ";AACA,OAAS,KAAAA,MAAS,eAClB,OAAS,cAAAC,MAAuC,QAChD,OAAS,MAAAC,EAAI,aAAAC,MAAiB,+BAC9B,OAAS,eAAAC,MAAmB,yBAC5B,OAAS,UAAAC,MAAc,qBAqBZ,cAAAC,EAWD,QAAAC,MAXC,oBAFJ,SAASC,EAAkBC,EAAyBC,EAAiCC,EAAsB,CAChH,IAAMC,EAAUX,EAA4C,SAAuB,CAAE,UAAAY,EAAW,GAAGC,CAAM,EAAGC,EAAK,CAC/G,OAAOT,EAACG,EAAK,QAAL,CAAa,IAAKM,EAAM,GAAGD,EAAO,UAAWX,EAAU,MAAMO,CAAI,WAAYG,CAAS,EAAG,CACnG,CAAC,EAEKG,EAAQf,EAAiE,SAC7E,CAAE,UAAAgB,EAAW,MAAAC,EAAQ,GAAM,WAAAC,EAAa,QAAS,UAAAN,EAAW,SAAAO,EAAU,GAAGN,CAAM,EAC/EC,EACA,CACA,OACER,EAACE,EAAK,OAAL,CAAY,UAAWQ,EACtB,UAAAX,EAACG,EAAK,SAAL,CAAc,UAAU,qBAAqB,EAC9CH,EAACG,EAAK,SAAL,CAAc,UAAWP,EAAG,qBAAsBgB,GAAS,iBAAiB,EAC3E,SAAAX,EAACE,EAAK,MAAL,CAAW,IAAKM,EAAM,GAAGD,EAAO,UAAWX,EAAU,sBAAsBO,CAAI,kCAAmCG,CAAS,EAE1H,UAAAP,EAACF,EAAY,SAAZ,CAAqB,MAAM,KAAM,SAAAgB,EAAS,EAC1CT,EACCL,EAACG,EAAK,MAAL,CAAW,UAAU,cAAc,OAAQH,EAACD,EAAA,CAAO,QAAQ,QAAQ,KAAK,KAAK,KAAMC,EAACN,EAAA,EAAE,EAAI,aAAYmB,EAAY,EAAI,EACrH,MACN,EACF,GACF,CAEJ,CAAC,EAEKE,EAAQpB,EAA2C,SAAqB,CAAE,UAAAY,EAAW,GAAGC,CAAM,EAAGC,EAAK,CAC1G,OAAOT,EAACG,EAAK,MAAL,CAAW,IAAKM,EAAM,GAAGD,EAAO,UAAWX,EAAU,2BAA4BU,CAAS,EAAG,CACvG,CAAC,EAEKS,EAAcrB,EAAmD,SAA2B,CAAE,UAAAY,EAAW,GAAGC,CAAM,EAAGC,EAAK,CAC9H,OAAOT,EAACG,EAAK,YAAL,CAAiB,IAAKM,EAAM,GAAGD,EAAO,UAAWX,EAAU,gCAAiCU,CAAS,EAAG,CAClH,CAAC,EAEKU,EAAQtB,EAA0C,SAAqB,CAAE,UAAAY,EAAW,GAAGC,CAAM,EAAGC,EAAK,CACzG,OAAOT,EAACG,EAAK,MAAL,CAAW,IAAKM,EAAM,GAAGD,EAAO,UAAWX,EAAU,MAAMO,CAAI,SAAUG,CAAS,EAAG,CAC/F,CAAC,EAEKW,EAASvB,EAAwC,SAAsB,CAAE,UAAAY,EAAW,GAAGC,CAAM,EAAGC,EAAK,CACzG,OAAOT,EAAC,OAAI,IAAKS,EAAM,GAAGD,EAAO,UAAWZ,EAAG,mBAAoBW,CAAS,EAAG,CACjF,CAAC,EAED,MAAO,CAAE,KAAMJ,EAAK,KAAM,QAAAG,EAAS,MAAAI,EAAO,MAAAK,EAAO,YAAAC,EAAa,MAAAC,EAAO,OAAAC,CAAO,CAC9E",
|
|
6
|
+
"names": ["X", "forwardRef", "cx", "withClass", "SizeContext", "Button", "jsx", "jsxs", "createDialogParts", "Base", "name", "closeButton", "Trigger", "className", "props", "ref", "Popup", "container", "sheet", "closeLabel", "children", "Title", "Description", "Close", "Footer"]
|
|
7
7
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import{Popover as s}from"@base-ui/react/popover";import{forwardRef as p}from"react";import{withClass as i}from"../../internal/class-name.js";import{splitPositioner as
|
|
2
|
+
import{Popover as s}from"@base-ui/react/popover";import{forwardRef as p}from"react";import{withClass as i}from"../../internal/class-name.js";import{splitPositioner as v}from"../../internal/popup.js";import{SizeContext as f}from"../../internal/size.js";import{popoverClasses as u}from"./classes.js";import{jsx as t}from"react/jsx-runtime";var P=u(),D=s.Root,H=p(function({className:o,...e},r){return t(s.Trigger,{ref:r,...e,className:i("mu-popover-trigger",o)})}),M=p(function({container:o,className:e,children:r,...a},c){let[l,m]=v(a);return t(s.Portal,{container:o,children:t(s.Positioner,{sideOffset:6,...l,className:"mu-positioner",children:t(s.Popup,{ref:c,...m,className:i(P.popup,e),children:t(f.Provider,{value:"md",children:r})})})})}),N=p(function({className:o,...e},r){return t(s.Title,{ref:r,...e,className:i(P.title,o)})}),h=p(function({className:o,...e},r){return t(s.Description,{ref:r,...e,className:i(P.description,o)})}),E=p(function({className:o,...e},r){return t(s.Close,{ref:r,...e,className:i("mu-popover-close",o)})});export{E as Close,h as Description,M as Popup,D as Root,N as Title,H as Trigger};
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/components/popover/index.tsx"],
|
|
4
|
-
"sourcesContent": ["import { Popover as BasePopover } from '@base-ui/react/popover';\nimport { forwardRef } from 'react';\nimport { withClass } from '../../internal/class-name';\nimport { splitPositioner } from '../../internal/popup';\nimport { popoverClasses } from './classes';\n\nconst classes = /* @__PURE__ */ popoverClasses();\n\nexport type RootProps = BasePopover.Root.Props;\n\n/** Groups a trigger and its popover. */\nexport const Root = BasePopover.Root;\n\nexport interface TriggerProps extends BasePopover.Trigger.Props {}\n\n/** Opens the popover. Usually `render={<Button />}`. */\nexport const Trigger = forwardRef<HTMLButtonElement, TriggerProps>(function PopoverTrigger({ className, ...props }, ref) {\n return <BasePopover.Trigger ref={ref} {...props} className={withClass('mu-popover-trigger', className)} />;\n});\n\ntype PositionerProps = Pick<\n BasePopover.Positioner.Props,\n 'side' | 'align' | 'collisionAvoidance' | 'collisionBoundary' | 'sticky' | 'anchor' | 'positionMethod' | 'disableAnchorTracking'\n>;\n\nexport interface PopupProps extends BasePopover.Popup.Props, PositionerProps {\n /** Where the popover is portaled to. Default `document.body`. */\n container?: BasePopover.Portal.Props['container'];\n}\n\n/** The panel: a surface with `shadow-pop`, `popover-width` wide. */\nexport const Popup = forwardRef<HTMLDivElement, PopupProps>(function PopoverPopup({ container, className, ...props }, ref) {\n const [positioner, rest] = splitPositioner(props);\n return (\n <BasePopover.Portal container={container}>\n <BasePopover.Positioner sideOffset={6} {...positioner} className=\"mu-positioner\">\n <BasePopover.Popup ref={ref} {...rest} className={withClass(classes.popup, className)}
|
|
5
|
-
"mappings": ";AAAA,OAAS,WAAWA,MAAmB,yBACvC,OAAS,cAAAC,MAAkB,QAC3B,OAAS,aAAAC,MAAiB,+BAC1B,OAAS,mBAAAC,MAAuB,0BAChC,OAAS,kBAAAC,MAAsB,eAatB,cAAAC,MAAA,oBAXT,IAAMC,EAA0BF,EAAe,EAKlCG,
|
|
6
|
-
"names": ["BasePopover", "forwardRef", "withClass", "splitPositioner", "popoverClasses", "jsx", "classes", "Root", "Trigger", "className", "props", "ref", "Popup", "container", "positioner", "rest", "Title", "Description", "Close"]
|
|
4
|
+
"sourcesContent": ["import { Popover as BasePopover } from '@base-ui/react/popover';\nimport { forwardRef } from 'react';\nimport { withClass } from '../../internal/class-name';\nimport { splitPositioner } from '../../internal/popup';\nimport { SizeContext } from '../../internal/size';\nimport { popoverClasses } from './classes';\n\nconst classes = /* @__PURE__ */ popoverClasses();\n\nexport type RootProps = BasePopover.Root.Props;\n\n/** Groups a trigger and its popover. */\nexport const Root = BasePopover.Root;\n\nexport interface TriggerProps extends BasePopover.Trigger.Props {}\n\n/** Opens the popover. Usually `render={<Button />}`. */\nexport const Trigger = forwardRef<HTMLButtonElement, TriggerProps>(function PopoverTrigger({ className, ...props }, ref) {\n return <BasePopover.Trigger ref={ref} {...props} className={withClass('mu-popover-trigger', className)} />;\n});\n\ntype PositionerProps = Pick<\n BasePopover.Positioner.Props,\n 'side' | 'align' | 'collisionAvoidance' | 'collisionBoundary' | 'sticky' | 'anchor' | 'positionMethod' | 'disableAnchorTracking'\n>;\n\nexport interface PopupProps extends BasePopover.Popup.Props, PositionerProps {\n /** Where the popover is portaled to. Default `document.body`. */\n container?: BasePopover.Portal.Props['container'];\n}\n\n/** The panel: a surface with `shadow-pop`, `popover-width` wide. */\nexport const Popup = forwardRef<HTMLDivElement, PopupProps>(function PopoverPopup({ container, className, children, ...props }, ref) {\n const [positioner, rest] = splitPositioner(props);\n return (\n <BasePopover.Portal container={container}>\n <BasePopover.Positioner sideOffset={6} {...positioner} className=\"mu-positioner\">\n <BasePopover.Popup ref={ref} {...rest} className={withClass(classes.popup, className)}>\n {/* The panel is md wherever it is declared: React context crosses the portal, so a sm toolbar's size would leak in. */}\n <SizeContext.Provider value=\"md\">{children}</SizeContext.Provider>\n </BasePopover.Popup>\n </BasePopover.Positioner>\n </BasePopover.Portal>\n );\n});\n\nexport interface TitleProps extends BasePopover.Title.Props {}\n\n/** The popover's heading, an `<h2>`. Names the popup for screen readers. */\nexport const Title = forwardRef<HTMLHeadingElement, TitleProps>(function PopoverTitle({ className, ...props }, ref) {\n return <BasePopover.Title ref={ref} {...props} className={withClass(classes.title, className)} />;\n});\n\nexport interface DescriptionProps extends BasePopover.Description.Props {}\n\n/** One line under the title, a `<p>`. */\nexport const Description = forwardRef<HTMLParagraphElement, DescriptionProps>(function PopoverDescription({ className, ...props }, ref) {\n return <BasePopover.Description ref={ref} {...props} className={withClass(classes.description, className)} />;\n});\n\nexport interface CloseProps extends BasePopover.Close.Props {}\n\n/** Closes the popover. Usually `render={<Button />}`. */\nexport const Close = forwardRef<HTMLButtonElement, CloseProps>(function PopoverClose({ className, ...props }, ref) {\n return <BasePopover.Close ref={ref} {...props} className={withClass('mu-popover-close', className)} />;\n});\n"],
|
|
5
|
+
"mappings": ";AAAA,OAAS,WAAWA,MAAmB,yBACvC,OAAS,cAAAC,MAAkB,QAC3B,OAAS,aAAAC,MAAiB,+BAC1B,OAAS,mBAAAC,MAAuB,0BAChC,OAAS,eAAAC,MAAmB,yBAC5B,OAAS,kBAAAC,MAAsB,eAatB,cAAAC,MAAA,oBAXT,IAAMC,EAA0BF,EAAe,EAKlCG,EAAOR,EAAY,KAKnBS,EAAUR,EAA4C,SAAwB,CAAE,UAAAS,EAAW,GAAGC,CAAM,EAAGC,EAAK,CACvH,OAAON,EAACN,EAAY,QAAZ,CAAoB,IAAKY,EAAM,GAAGD,EAAO,UAAWT,EAAU,qBAAsBQ,CAAS,EAAG,CAC1G,CAAC,EAaYG,EAAQZ,EAAuC,SAAsB,CAAE,UAAAa,EAAW,UAAAJ,EAAW,SAAAK,EAAU,GAAGJ,CAAM,EAAGC,EAAK,CACnI,GAAM,CAACI,EAAYC,CAAI,EAAId,EAAgBQ,CAAK,EAChD,OACEL,EAACN,EAAY,OAAZ,CAAmB,UAAWc,EAC7B,SAAAR,EAACN,EAAY,WAAZ,CAAuB,WAAY,EAAI,GAAGgB,EAAY,UAAU,gBAC/D,SAAAV,EAACN,EAAY,MAAZ,CAAkB,IAAKY,EAAM,GAAGK,EAAM,UAAWf,EAAUK,EAAQ,MAAOG,CAAS,EAElF,SAAAJ,EAACF,EAAY,SAAZ,CAAqB,MAAM,KAAM,SAAAW,EAAS,EAC7C,EACF,EACF,CAEJ,CAAC,EAKYG,EAAQjB,EAA2C,SAAsB,CAAE,UAAAS,EAAW,GAAGC,CAAM,EAAGC,EAAK,CAClH,OAAON,EAACN,EAAY,MAAZ,CAAkB,IAAKY,EAAM,GAAGD,EAAO,UAAWT,EAAUK,EAAQ,MAAOG,CAAS,EAAG,CACjG,CAAC,EAKYS,EAAclB,EAAmD,SAA4B,CAAE,UAAAS,EAAW,GAAGC,CAAM,EAAGC,EAAK,CACtI,OAAON,EAACN,EAAY,YAAZ,CAAwB,IAAKY,EAAM,GAAGD,EAAO,UAAWT,EAAUK,EAAQ,YAAaG,CAAS,EAAG,CAC7G,CAAC,EAKYU,EAAQnB,EAA0C,SAAsB,CAAE,UAAAS,EAAW,GAAGC,CAAM,EAAGC,EAAK,CACjH,OAAON,EAACN,EAAY,MAAZ,CAAkB,IAAKY,EAAM,GAAGD,EAAO,UAAWT,EAAU,mBAAoBQ,CAAS,EAAG,CACtG,CAAC",
|
|
6
|
+
"names": ["BasePopover", "forwardRef", "withClass", "splitPositioner", "SizeContext", "popoverClasses", "jsx", "classes", "Root", "Trigger", "className", "props", "ref", "Popup", "container", "children", "positioner", "rest", "Title", "Description", "Close"]
|
|
7
7
|
}
|
package/llms-full.txt
CHANGED
|
@@ -26,6 +26,7 @@ It is not a general-purpose UI library for outside developers. Components are ge
|
|
|
26
26
|
| [States](states.md) | Hover, pressed, loading, disabled, invalid and focus |
|
|
27
27
|
| [Icons](icons.md) | Lucide, sizes, fixed stroke widths and accessibility |
|
|
28
28
|
| [Components](components/README.md) | The 28 components, what was left out, and one page per component |
|
|
29
|
+
| [Using the components](usage.md) | Which component to pick (overlays, controls, feedback), recipes for combining them, size, emphasis, an accessibility checklist, common mistakes and a map from other UI kits |
|
|
29
30
|
| [Patterns](patterns.md) | Page, form, entry, confirmation, loading, empty state and mobile |
|
|
30
31
|
| [Building on it](building.md) | Base UI, loading the CSS, scoping inside a host page, theme selection and API conventions |
|
|
31
32
|
| [Building your own component](custom-components.md) | How an app builds a part the package doesn't ship: Base UI, class functions, public classes, row variables and Tailwind CSS |
|
|
@@ -581,6 +582,8 @@ Components follow the [API conventions](../building.md#api-conventions): parts w
|
|
|
581
582
|
|
|
582
583
|
## Choosing between similar components
|
|
583
584
|
|
|
585
|
+
The short version. [Using the components](../usage.md) has the full decision guides, recipes and common mistakes.
|
|
586
|
+
|
|
584
587
|
| Need | Use |
|
|
585
588
|
| --- | --- |
|
|
586
589
|
| Start an action | Button |
|
|
@@ -835,7 +838,8 @@ Base UI's props pass through: `open`, `defaultOpen`, `onOpenChange`, `modal` on
|
|
|
835
838
|
- Act in `onClick`. Labels are verbs in sentence case.
|
|
836
839
|
- Put destructive items last, after a Separator, with `danger`. Confirm them with an [AlertDialog](dialog.md).
|
|
837
840
|
- CheckboxItems and RadioItems stay open on click, so people can change several.
|
|
838
|
-
- For picking a value saved with a form, use [Select](select.md).
|
|
841
|
+
- For picking a value saved with a form, use [Select](select.md). For content that stays open while people use it (inputs, text, several controls), use [Popover](popover.md). See [Menu or Popover](../usage.md#menu-or-popover).
|
|
842
|
+
- To open a Dialog from an item, keep the Dialog outside the Menu and control its `open`. See [the recipe](../usage.md#a-menu-item-that-opens-a-dialog).
|
|
839
843
|
- Arrow keys move the highlight; typing jumps to an item; Escape and an outside click close it; focus returns to the trigger.
|
|
840
844
|
- An app's own floating list, such as an editor's suggestion list, takes `menuClasses()`: `popup` on the list, `item` on each row, and `data-highlighted` on the active row. See [Class functions](../custom-components.md#class-functions).
|
|
841
845
|
|
|
@@ -1508,6 +1512,7 @@ Base UI's props pass through: `open`, `defaultOpen`, `onOpenChange`, `modal`, `d
|
|
|
1508
1512
|
- Use AlertDialog for confirmations; see the [confirmation pattern](../patterns.md#confirmation). Open it from a Menu item with `open` and `onOpenChange`.
|
|
1509
1513
|
- Escape and a backdrop click close a Dialog. While a form has unsaved input, keep it open from `onOpenChange`.
|
|
1510
1514
|
- Focus moves into the dialog and returns to the trigger on close.
|
|
1515
|
+
- The panel is always md: controls inside it ignore the size of a Root the Dialog is declared in (a sm Table). Pass `size` to a control inside only when it should differ.
|
|
1511
1516
|
|
|
1512
1517
|
## Style
|
|
1513
1518
|
|
|
@@ -1565,6 +1570,7 @@ Base UI's props pass through: `open`, `defaultOpen`, `onOpenChange`, `modal` on
|
|
|
1565
1570
|
|
|
1566
1571
|
- Use it for a few controls that belong to one thing. For a list of actions, use [Menu](menu.md). For a decision that blocks the page, use [Dialog](dialog.md).
|
|
1567
1572
|
- Give it a `Popover.Title`, or `aria-label` on the popup.
|
|
1573
|
+
- The panel is always md: controls inside it ignore the size of the Root around the Popover (a sm toolbar). Pass `size` to a control inside only when it should differ.
|
|
1568
1574
|
- It closes on Escape and on a click outside; focus returns to the trigger.
|
|
1569
1575
|
- An app's own floating panel, such as an emoji picker, takes `popoverClasses()`. See [Class functions](../custom-components.md#class-functions).
|
|
1570
1576
|
|
|
@@ -1624,6 +1630,7 @@ Base UI's props pass through: `open`, `defaultOpen`, `onOpenChange`, `disabled`
|
|
|
1624
1630
|
- Never put essential information only in a tooltip: it doesn't open on touch screens.
|
|
1625
1631
|
- For text that explains something, use a [Popover](popover.md) instead.
|
|
1626
1632
|
- Wrap a toolbar, or the whole app, in one `Tooltip.Provider`.
|
|
1633
|
+
- On a trigger that also opens a Menu, Popover or Dialog, stack the triggers: `<Tooltip.Trigger render={<Menu.Trigger render={<Button … />} />} />`. See [the recipe](../usage.md#an-icon-only-button-with-a-tooltip-and-a-menu).
|
|
1627
1634
|
|
|
1628
1635
|
## Style
|
|
1629
1636
|
|
|
@@ -2055,6 +2062,377 @@ Every native attribute passes through.
|
|
|
2055
2062
|
- Tags: `ink-muted` on `well`, `radius-xs`. They read as words, not badges.
|
|
2056
2063
|
- Sizes are custom properties set on `.mu-prose` in the foundation (`--prose-fs`, `--prose-lh`, heading sizes).
|
|
2057
2064
|
|
|
2065
|
+
<!-- docs/usage.md -->
|
|
2066
|
+
|
|
2067
|
+
# Using the components
|
|
2068
|
+
|
|
2069
|
+
How to pick the right component for a screen and put components together. Read it before building or migrating a screen in an app. Each component's own page has its props and rules; this page decides between them.
|
|
2070
|
+
|
|
2071
|
+
## Before you build a screen
|
|
2072
|
+
|
|
2073
|
+
1. Find the [pattern](patterns.md) the screen follows: page, form, entry, confirmation, loading, empty state, mobile. Follow it before choosing components.
|
|
2074
|
+
2. Pick each component with the guides below. When two seem to fit, the first row that matches wins.
|
|
2075
|
+
3. Import components from `@usememos/ui` and icons from `lucide-react`. Don't wrap a component to restyle it.
|
|
2076
|
+
4. Use `className` and `style` for layout only: width, margin, grid or flex placement. Don't change a component's color, height, padding, radius, shadow, font size or focus style. If the look is wrong, the component or its page is wrong; change the package, not the screen.
|
|
2077
|
+
5. A part the package doesn't ship is built with [Building your own component](custom-components.md), never by copying a component's CSS.
|
|
2078
|
+
|
|
2079
|
+
## Picking something that floats
|
|
2080
|
+
|
|
2081
|
+
The most common mistake is reaching for the wrong overlay. Decide by what the floating thing holds and how it closes.
|
|
2082
|
+
|
|
2083
|
+
| Component | Holds | Opens on | Closes when | Blocks the page | Use it for |
|
|
2084
|
+
| --- | --- | --- | --- | --- | --- |
|
|
2085
|
+
| [Tooltip](components/tooltip.md) | Two or three words of text, and a shortcut | Hover or keyboard focus | The pointer or focus leaves | No | Naming an icon-only control |
|
|
2086
|
+
| [Menu](components/menu.md) | A list of commands | Click on its trigger | One command is picked, Escape, a click outside | No | Actions on one thing: "Pin", "Copy link", "Delete" |
|
|
2087
|
+
| [Select](components/select.md), [Combobox](components/combobox.md) | A list of values | Click or typing | One value is picked | No | Choosing a value that a form or setting keeps |
|
|
2088
|
+
| [Popover](components/popover.md) | Any small content: text, a few controls | Click on its trigger | Escape, a click outside, or its own Close | No | A share panel, a link editor, a filter panel, an explanation |
|
|
2089
|
+
| [Dialog](components/dialog.md) | A short form or a decision | An action | Its buttons, Escape, a backdrop click | Yes | Creating something, editing settings of one thing |
|
|
2090
|
+
| [AlertDialog](components/dialog.md) | A question and two buttons | An action | Only its buttons | Yes | Confirming a destructive or irreversible action |
|
|
2091
|
+
| [Toast](components/toast.md) | A sentence about what happened, at most one action | The app, after something happened | A timeout, its X, or its action | No | "Note archived" with Undo; "Couldn't sync" |
|
|
2092
|
+
|
|
2093
|
+
### Menu or Popover
|
|
2094
|
+
|
|
2095
|
+
- **Menu: picking an item runs a command and closes the menu.** Items are verbs. Arrow keys move between items; typing jumps to one. Nothing inside a Menu takes text or holds focus.
|
|
2096
|
+
- **Popover: the content stays open while people use it.** It can hold text, inputs, buttons, a list of toggles. People tab through it like a small page.
|
|
2097
|
+
- An input, a checkbox list with an Apply button, or explanatory text inside a Menu means it should be a Popover.
|
|
2098
|
+
- A Popover whose content is only a list of actions should be a Menu.
|
|
2099
|
+
- A Menu may hold `Menu.CheckboxItem`s and `Menu.RadioItem`s for view options ("Show archived", "Sort by"). They stay open on click. Use them only for options of the view the menu belongs to, not for form values.
|
|
2100
|
+
|
|
2101
|
+
### Menu or Select
|
|
2102
|
+
|
|
2103
|
+
- **Menu runs commands; Select keeps a value.** A Select's trigger shows the chosen value, it sits in a [Field](components/field.md) with a label, and its value is submitted with a form.
|
|
2104
|
+
- A "Sort by" control in a toolbar that changes a view is a Menu with a `Menu.RadioGroup`, or a [ToggleGroup](components/toggle-group.md) when the options fit in a row.
|
|
2105
|
+
- A "Visibility" field in a form is a Select.
|
|
2106
|
+
|
|
2107
|
+
### Popover, Dialog or a page
|
|
2108
|
+
|
|
2109
|
+
- **Popover**: one to three controls about the thing next to it, changes that apply as they are made or are trivial to undo.
|
|
2110
|
+
- **Dialog**: more than three controls, a Save or Create button, or a decision that must be made before going on.
|
|
2111
|
+
- **A page**: more than about six fields, sections, or content people need to link to.
|
|
2112
|
+
- Don't open a Dialog from a Dialog. Replace the content, or close the first one. The one exception is an AlertDialog that confirms discarding the first one's input.
|
|
2113
|
+
|
|
2114
|
+
### Tooltip or Popover
|
|
2115
|
+
|
|
2116
|
+
- **Tooltip**: a label for a control. It has no interactive content and never opens on touch screens, so it must never be the only place a fact appears.
|
|
2117
|
+
- **Popover**: anything people need to read, anything with a link or a button, or anything that must work on touch. For a hover card ("show the person on hover"), use a Popover with `openOnHover` on its trigger, and make sure a click shows the same thing.
|
|
2118
|
+
|
|
2119
|
+
### Dialog or AlertDialog
|
|
2120
|
+
|
|
2121
|
+
- **AlertDialog** for "Are you sure?": deleting, discarding unsaved input, revoking a token. It has no X and ignores the backdrop, so people must answer.
|
|
2122
|
+
- **Dialog** for everything else. A Dialog with a form keeps itself open while it has unsaved input (see [Recipes](#form-in-a-dialog)).
|
|
2123
|
+
- Don't confirm actions that can be undone. Run them and show a Toast with Undo instead.
|
|
2124
|
+
|
|
2125
|
+
### Toast or an inline message
|
|
2126
|
+
|
|
2127
|
+
- **Toast**: the result of an action people just took, when the screen doesn't already show it. "Note archived" (the note disappeared), "Link copied" (nothing else changed).
|
|
2128
|
+
- **Inline**: anything tied to a place on the screen. A field's error goes in `Field.Error` under the field. A page that fails to load says so in the page, with a Retry button.
|
|
2129
|
+
- Don't toast what the screen already shows. A saved setting whose Switch moved needs no toast.
|
|
2130
|
+
- A toast never asks a question. A decision is an AlertDialog.
|
|
2131
|
+
|
|
2132
|
+
## Picking a control
|
|
2133
|
+
|
|
2134
|
+
### Starting an action or going somewhere
|
|
2135
|
+
|
|
2136
|
+
| Need | Use | Not |
|
|
2137
|
+
| --- | --- | --- |
|
|
2138
|
+
| Run an action | [Button](components/button.md) | A Link with an `onClick` |
|
|
2139
|
+
| Go to another page | [Link](components/link.md) | A Button with `navigate()` in `onClick` |
|
|
2140
|
+
| Go to another page, styled as an action ("Get started") | An `<a>` (or router link) with `buttonClasses()` | `Button` with `render={<a />}` |
|
|
2141
|
+
| Several actions on one thing | A ghost Button that opens a [Menu](components/menu.md) | A row of icon buttons for rare actions |
|
|
2142
|
+
| Turn one option on or off in a toolbar | [Toggle](components/toggle.md) | A Button that changes its own label |
|
|
2143
|
+
|
|
2144
|
+
### Choosing a value
|
|
2145
|
+
|
|
2146
|
+
Answer in order; the first yes decides.
|
|
2147
|
+
|
|
2148
|
+
1. **Does it save the moment it changes (a settings page)?** On or off: [Switch](components/switch.md). One of a few: [Select](components/select.md) that saves in `onValueChange`.
|
|
2149
|
+
2. **Is it on or off, saved with a form?** [Checkbox](components/checkbox.md).
|
|
2150
|
+
3. **Is it one of 2 or 3 options, with room to show them or each needing a description?** [RadioGroup](components/radio-group.md).
|
|
2151
|
+
4. **Is it one of up to about 10 options?** [Select](components/select.md).
|
|
2152
|
+
5. **Is it one of a long list, or do people know what they're looking for and type it?** [Combobox](components/combobox.md).
|
|
2153
|
+
6. **Is it several of a few options?** Checkboxes in a [Fieldset](components/field.md).
|
|
2154
|
+
7. **Is it a view or filter on the current page, not a stored value?** One choice: [ToggleGroup](components/toggle-group.md) without `multiple`. Stacking filters: [Toggle](components/toggle.md)s, or a ToggleGroup with `multiple`.
|
|
2155
|
+
8. **Is it text?** One line: [Input](components/input.md). A few plain lines: [Textarea](components/textarea.md). Formatted content: the editor (`@usememos/editor`) with [Prose](components/prose.md).
|
|
2156
|
+
9. **Is it a number?** An Input with `type="number"` and `inputMode="numeric"`, or a Select of the sensible values. There is no Slider or Stepper.
|
|
2157
|
+
|
|
2158
|
+
### Grouping and moving between sections
|
|
2159
|
+
|
|
2160
|
+
| Need | Use | Not |
|
|
2161
|
+
| --- | --- | --- |
|
|
2162
|
+
| Sections of one page (Account, Notifications) | [Tabs](components/tabs.md) | ToggleGroup |
|
|
2163
|
+
| A view or filter of the same content (All, Pinned, Archived) | [ToggleGroup](components/toggle-group.md) without `multiple` | Tabs |
|
|
2164
|
+
| Navigation between pages | [Link](components/link.md)s in the app's sidebar | Tabs |
|
|
2165
|
+
| Details or advanced settings people rarely need | [Collapsible](components/collapsible.md) | A Dialog |
|
|
2166
|
+
| Several sections, one open at a time | Collapsibles, controlled together | A third-party accordion |
|
|
2167
|
+
| Separating groups of content | `space-6` of whitespace, then a section title, then a [Separator](components/separator.md) if still unclear | A box, border or card |
|
|
2168
|
+
| Data with columns (tokens, users, webhooks) | [Table](components/table.md) | A list of boxes |
|
|
2169
|
+
| A list of content (notes, comments) | [Entries](patterns.md#entry) | A Table |
|
|
2170
|
+
|
|
2171
|
+
## Picking feedback
|
|
2172
|
+
|
|
2173
|
+
| Situation | Use |
|
|
2174
|
+
| --- | --- |
|
|
2175
|
+
| A button's action is waiting on a server | The Button's `loading`, with `loadingLabel` ("Saving") |
|
|
2176
|
+
| A list or page is loading | [Skeleton](components/skeleton.md)s in the shape of the content, and `aria-busy` on the region |
|
|
2177
|
+
| Something short is running with no control to put it in | [Spinner](components/spinner.md) with a `label` |
|
|
2178
|
+
| An action finished and the screen doesn't show it | [Toast](components/toast.md), neutral or `success` |
|
|
2179
|
+
| An action failed | Toast with `type: "danger"` and `timeout: 0`; a field's own error in `Field.Error` instead |
|
|
2180
|
+
| A field's value is wrong | `invalid` on `Field.Root` and a `Field.Error` that says how to fix it |
|
|
2181
|
+
| A thing has a status (Synced, Expired) | [Badge](components/badge.md) with a tone and a word |
|
|
2182
|
+
| A list is empty | The [empty state](patterns.md#empty-state) pattern |
|
|
2183
|
+
| An action is about to destroy something | [AlertDialog](components/dialog.md), then a Toast after it runs |
|
|
2184
|
+
|
|
2185
|
+
Waits under about 300ms show nothing. Status colors (`success`, `warning`, `danger`) appear only with words or an icon, never as the only signal.
|
|
2186
|
+
|
|
2187
|
+
## Recipes
|
|
2188
|
+
|
|
2189
|
+
### Trigger through `render`
|
|
2190
|
+
|
|
2191
|
+
Every `Trigger` and `Close` renders the element given in `render`. Pass the Button there; don't put it inside.
|
|
2192
|
+
|
|
2193
|
+
```tsx
|
|
2194
|
+
// Do: one <button>
|
|
2195
|
+
<Menu.Trigger render={<Button variant="ghost" icon={<Ellipsis />} aria-label="More actions" />} />
|
|
2196
|
+
|
|
2197
|
+
// Don't: a <button> inside a <button>
|
|
2198
|
+
<Menu.Trigger><Button variant="ghost" icon={<Ellipsis />} aria-label="More actions" /></Menu.Trigger>
|
|
2199
|
+
```
|
|
2200
|
+
|
|
2201
|
+
### An icon-only button with a tooltip and a menu
|
|
2202
|
+
|
|
2203
|
+
Stack the triggers with `render`. One `<button>` gets both behaviors.
|
|
2204
|
+
|
|
2205
|
+
```tsx
|
|
2206
|
+
<Menu.Root>
|
|
2207
|
+
<Tooltip.Root>
|
|
2208
|
+
<Tooltip.Trigger
|
|
2209
|
+
render={<Menu.Trigger render={<Button variant="ghost" icon={<Ellipsis />} aria-label="More actions" />} />}
|
|
2210
|
+
/>
|
|
2211
|
+
<Tooltip.Popup>More actions</Tooltip.Popup>
|
|
2212
|
+
</Tooltip.Root>
|
|
2213
|
+
<Menu.Popup align="end">…</Menu.Popup>
|
|
2214
|
+
</Menu.Root>
|
|
2215
|
+
```
|
|
2216
|
+
|
|
2217
|
+
The same works for a Dialog or Popover trigger: `<Tooltip.Trigger render={<Popover.Trigger render={<Button … />} />} />`.
|
|
2218
|
+
|
|
2219
|
+
### A menu item that opens a dialog
|
|
2220
|
+
|
|
2221
|
+
Keep the Dialog outside the Menu and control it with `open`. The Menu closes when the item is picked, so a Dialog inside `Menu.Popup` would unmount with it.
|
|
2222
|
+
|
|
2223
|
+
```tsx
|
|
2224
|
+
const [confirming, setConfirming] = useState(false);
|
|
2225
|
+
|
|
2226
|
+
<Menu.Root>
|
|
2227
|
+
<Menu.Trigger render={<Button variant="ghost" icon={<Ellipsis />} aria-label="More actions" />} />
|
|
2228
|
+
<Menu.Popup align="end">
|
|
2229
|
+
<Menu.Item icon={<Pin />} onClick={pin}>Pin</Menu.Item>
|
|
2230
|
+
<Menu.Separator />
|
|
2231
|
+
<Menu.Item icon={<Trash2 />} danger onClick={() => setConfirming(true)}>Delete</Menu.Item>
|
|
2232
|
+
</Menu.Popup>
|
|
2233
|
+
</Menu.Root>
|
|
2234
|
+
|
|
2235
|
+
<AlertDialog.Root open={confirming} onOpenChange={setConfirming}>
|
|
2236
|
+
<AlertDialog.Popup>
|
|
2237
|
+
<AlertDialog.Title>Delete this note?</AlertDialog.Title>
|
|
2238
|
+
<AlertDialog.Description>It moves to the trash for 30 days.</AlertDialog.Description>
|
|
2239
|
+
<AlertDialog.Footer>
|
|
2240
|
+
<AlertDialog.Close render={<Button variant="ghost">Cancel</Button>} />
|
|
2241
|
+
<Button variant="danger" loading={deleting} loadingLabel="Deleting" onClick={remove}>Delete note</Button>
|
|
2242
|
+
</AlertDialog.Footer>
|
|
2243
|
+
</AlertDialog.Popup>
|
|
2244
|
+
</AlertDialog.Root>
|
|
2245
|
+
```
|
|
2246
|
+
|
|
2247
|
+
`remove` closes the dialog with `setConfirming(false)` once the server answers, then shows a Toast. Use `AlertDialog.Close render={<Button variant="danger" />}` only when the action can't fail.
|
|
2248
|
+
|
|
2249
|
+
### Form in a dialog
|
|
2250
|
+
|
|
2251
|
+
```tsx
|
|
2252
|
+
<Dialog.Root
|
|
2253
|
+
open={open}
|
|
2254
|
+
onOpenChange={(next) => {
|
|
2255
|
+
if (!next && dirty) return; // keep unsaved input; Cancel resets and closes
|
|
2256
|
+
setOpen(next);
|
|
2257
|
+
}}
|
|
2258
|
+
>
|
|
2259
|
+
<Dialog.Trigger render={<Button>Create access token</Button>} />
|
|
2260
|
+
<Dialog.Popup>
|
|
2261
|
+
<Dialog.Title>Create access token</Dialog.Title>
|
|
2262
|
+
<Dialog.Description>Use it to call the API from scripts.</Dialog.Description>
|
|
2263
|
+
<form id="create-token" onSubmit={create}>
|
|
2264
|
+
<Field.Root invalid={!!error}>
|
|
2265
|
+
<Field.Label>Description</Field.Label>
|
|
2266
|
+
<Input value={description} onValueChange={setDescription} />
|
|
2267
|
+
<Field.Error match>{error}</Field.Error>
|
|
2268
|
+
</Field.Root>
|
|
2269
|
+
</form>
|
|
2270
|
+
<Dialog.Footer>
|
|
2271
|
+
<Button variant="ghost" onClick={cancel}>Cancel</Button>
|
|
2272
|
+
<Button variant="primary" type="submit" form="create-token" loading={saving} loadingLabel="Creating">Create</Button>
|
|
2273
|
+
</Dialog.Footer>
|
|
2274
|
+
</Dialog.Popup>
|
|
2275
|
+
</Dialog.Root>
|
|
2276
|
+
```
|
|
2277
|
+
|
|
2278
|
+
- One Field per control. The submit button is `primary`, named with its verb, and shows `loading`.
|
|
2279
|
+
- On a server error, keep the dialog open and show the error on the field it belongs to. Close it only on success.
|
|
2280
|
+
- On phones the dialog becomes a sheet by itself. Don't build a separate mobile layout.
|
|
2281
|
+
|
|
2282
|
+
### Settings that save at once
|
|
2283
|
+
|
|
2284
|
+
```tsx
|
|
2285
|
+
<Fieldset.Root>
|
|
2286
|
+
<Fieldset.Legend>Notifications</Fieldset.Legend>
|
|
2287
|
+
<Field.Root>
|
|
2288
|
+
<Field.Label>
|
|
2289
|
+
<Switch checked={mentions} onCheckedChange={saveMentions} /> Email me when I'm mentioned
|
|
2290
|
+
</Field.Label>
|
|
2291
|
+
<Field.Description>Sent at most once an hour.</Field.Description>
|
|
2292
|
+
</Field.Root>
|
|
2293
|
+
</Fieldset.Root>
|
|
2294
|
+
```
|
|
2295
|
+
|
|
2296
|
+
No Save button and no toast: the Switch shows the result. If saving fails, move the Switch back and show a danger Toast.
|
|
2297
|
+
|
|
2298
|
+
### A toolbar
|
|
2299
|
+
|
|
2300
|
+
```tsx
|
|
2301
|
+
<Tooltip.Provider>
|
|
2302
|
+
<ToggleGroup multiple size="sm" value={marks} onValueChange={setMarks} aria-label="Formatting">
|
|
2303
|
+
<Tooltip.Root>
|
|
2304
|
+
<Tooltip.Trigger render={<Toggle value="bold" icon={<Bold />} aria-label="Bold" />} />
|
|
2305
|
+
<Tooltip.Popup shortcut="⌘ B">Bold</Tooltip.Popup>
|
|
2306
|
+
</Tooltip.Root>
|
|
2307
|
+
…
|
|
2308
|
+
<Separator orientation="vertical" />
|
|
2309
|
+
…
|
|
2310
|
+
</ToggleGroup>
|
|
2311
|
+
</Tooltip.Provider>
|
|
2312
|
+
```
|
|
2313
|
+
|
|
2314
|
+
One `Tooltip.Provider` around the toolbar (or the app) so moving along it opens the next tooltip at once.
|
|
2315
|
+
|
|
2316
|
+
### Row and entry actions
|
|
2317
|
+
|
|
2318
|
+
- Put rare actions in a ghost "More actions" Menu trigger at the end of the row. Name it for the row: `aria-label={`Actions for ${token.name}`}`.
|
|
2319
|
+
- Show one or two frequent actions as xs or sm ghost Buttons beside it, each with a Tooltip.
|
|
2320
|
+
- On an entry the trigger appears on hover; on touch screens it is always visible (see [Entry](patterns.md#entry)).
|
|
2321
|
+
|
|
2322
|
+
### Inside a shadow root or a host page
|
|
2323
|
+
|
|
2324
|
+
In an extension or an embed, pass the same `container` to every `Popup` and to `Toast.Viewport`, so popups mount inside the shadow root and pick up its styles. See [Scoping inside a host page](building.md#scoping-inside-a-host-page).
|
|
2325
|
+
|
|
2326
|
+
```tsx
|
|
2327
|
+
<Menu.Popup container={shadowRoot}>…</Menu.Popup>
|
|
2328
|
+
<Toast.Viewport container={shadowRoot} />
|
|
2329
|
+
```
|
|
2330
|
+
|
|
2331
|
+
## Size
|
|
2332
|
+
|
|
2333
|
+
- **Set `size` once, on the outermost part that takes it.** `Menu.Root`, `Select.Root`, `Combobox.Root`, `Tabs.Root`, `Collapsible.Root`, `Table.Root`, `Field.Root`, `Fieldset.Root`, `RadioGroup` and `ToggleGroup` pass their size to every part and every Button, Toggle, Checkbox or Input inside them. Don't repeat it on the parts.
|
|
2334
|
+
- **Pick by place, not by importance.** `md` everywhere by default, including dialogs, forms, onboarding and marketing pages. `sm` for dense toolbars, table rows and filters inside panels. `xs` for secondary actions beside 13px text (entry footers, row actions). See [Control sizes](layout.md#control-sizes).
|
|
2335
|
+
- **Never make a control bigger to make it stronger.** Emphasis is the `primary` variant.
|
|
2336
|
+
- **Don't mix sizes in one row.** A Button beside an Input has the Input's size.
|
|
2337
|
+
- **Dialogs and Popovers start at md.** Their panels are md wherever they are declared, so a Dialog inside a `Table.Root size="sm"` still has md Buttons and fields. Set `size` inside the panel only for a part that should differ.
|
|
2338
|
+
- **Menus follow their surroundings.** A Menu, Select or Combobox without its own `size` takes the size of the Root around it, so a menu opened from a sm toolbar has sm rows.
|
|
2339
|
+
- Text entry, menus, tabs and tables have no `xs`; an inherited `xs` renders them at `sm`.
|
|
2340
|
+
|
|
2341
|
+
## Emphasis and color
|
|
2342
|
+
|
|
2343
|
+
- **One `primary` Button per view.** A view is a page, a dialog or a popover. Everything else is `neutral`, or `ghost` in toolbars and rows.
|
|
2344
|
+
- **`danger` confirms; it never starts.** The first click on "Delete" is a Menu item with `danger` or a neutral Button; the `danger` Button lives in the AlertDialog that follows.
|
|
2345
|
+
- **Dialog footers** are a ghost Cancel, then the action, named with its verb ("Delete note", "Create"), never "OK" or "Yes".
|
|
2346
|
+
- **Tone is status only.** Badge and Toast tones say success, warning or danger. Don't use them to highlight, decorate or color-code categories.
|
|
2347
|
+
- **Links are the only blue.** Don't color text, icons or backgrounds with `accent`.
|
|
2348
|
+
|
|
2349
|
+
## Accessibility checklist
|
|
2350
|
+
|
|
2351
|
+
Run through it for every screen.
|
|
2352
|
+
|
|
2353
|
+
- [ ] Every control has a name: a `Field.Label`, a visible label, or `aria-label`. Placeholders are never names.
|
|
2354
|
+
- [ ] Every icon-only Button and Toggle has an `aria-label` and a Tooltip with the same text.
|
|
2355
|
+
- [ ] Every group has a name: `aria-label` on a ToggleGroup and a `Tabs.List`; a `Fieldset.Legend`; a `Table.Caption` or `aria-label` on a Table; a `Popover.Title` or `aria-label`; a `Dialog.Title`.
|
|
2356
|
+
- [ ] Checkboxes, Switches and Radios sit inside their `Field.Label`, so the text is clickable.
|
|
2357
|
+
- [ ] Repeated triggers are named for their row ("Actions for Backup token"), not just "More actions".
|
|
2358
|
+
- [ ] No fact appears only in a Tooltip, only in color, or only in a placeholder.
|
|
2359
|
+
- [ ] Loading regions have `aria-busy`; loading Buttons have a `loadingLabel`.
|
|
2360
|
+
- [ ] Errors are in `Field.Error` or a danger Toast with `timeout: 0`, worded as how to fix them.
|
|
2361
|
+
- [ ] No `outline: none`, custom focus ring or `tabIndex` on a component. Focus, keyboard and ARIA come from Base UI; don't add key handlers that fight them.
|
|
2362
|
+
- [ ] Nothing pressable is built from a `<div>`. If the package has no component for it, build it on Base UI ([Building your own component](custom-components.md)).
|
|
2363
|
+
|
|
2364
|
+
## Common mistakes
|
|
2365
|
+
|
|
2366
|
+
| Don't | Do |
|
|
2367
|
+
| --- | --- |
|
|
2368
|
+
| An Input or checkbox list inside a Menu | A Popover |
|
|
2369
|
+
| A Popover holding only a list of actions | A Menu |
|
|
2370
|
+
| A Menu to choose a form value | A Select in a Field |
|
|
2371
|
+
| A Dialog for one or two quick controls | A Popover |
|
|
2372
|
+
| A Dialog for "Are you sure?" | An AlertDialog |
|
|
2373
|
+
| A confirmation for an action that can be undone | Run it, then a Toast with Undo |
|
|
2374
|
+
| A Tooltip with a link, a button or a paragraph | A Popover |
|
|
2375
|
+
| A Tooltip on text that isn't a control | Visible text, or a Popover |
|
|
2376
|
+
| A Toast for a field's error | `Field.Error` under the field |
|
|
2377
|
+
| A success Toast after every Switch | Nothing; the Switch shows it |
|
|
2378
|
+
| A Button that navigates | A Link, or an anchor with `buttonClasses()` |
|
|
2379
|
+
| `<Button render={<a href="…" />}>` | `<a className={buttonClasses()} href="…">` |
|
|
2380
|
+
| A Button nested inside a `Trigger` | `render={<Button … />}` on the Trigger |
|
|
2381
|
+
| A clickable Badge | A Toggle (filters) or a Link with `badgeClasses()` (tag pages) |
|
|
2382
|
+
| Tabs to filter a list | A ToggleGroup without `multiple` |
|
|
2383
|
+
| A ToggleGroup to switch page sections | Tabs |
|
|
2384
|
+
| A Switch in a form with a Save button | A Checkbox |
|
|
2385
|
+
| A Checkbox that saves at once | A Switch |
|
|
2386
|
+
| A Select with 30 options | A Combobox |
|
|
2387
|
+
| A Select with two options and room to spare | A RadioGroup |
|
|
2388
|
+
| A boxed card around each item | Whitespace, a section title, or a Separator |
|
|
2389
|
+
| A Spinner where a list will appear | Skeletons in the shape of the list |
|
|
2390
|
+
| `size="md"` or `"lg"` on a Button to emphasize it | `variant="primary"` at the row's size |
|
|
2391
|
+
| `size` on every part of a Menu or Select | `size` on its Root |
|
|
2392
|
+
| Two `primary` Buttons in one view | One `primary`, the rest `neutral` |
|
|
2393
|
+
| `danger` Button as the first click | A `danger` Menu item, then an AlertDialog |
|
|
2394
|
+
| A placeholder as the only label | `Field.Label`, or `aria-label` |
|
|
2395
|
+
| Color as the only signal of status | A Badge or Toast with a word and an icon |
|
|
2396
|
+
| `className` that changes a component's color, height or radius | Use the component as it is, or change the package |
|
|
2397
|
+
| Copying a component's CSS to make a lookalike | Base UI with a class function or public classes |
|
|
2398
|
+
| A `<div onClick>` as a control | A component, or Base UI's part with a class function |
|
|
2399
|
+
|
|
2400
|
+
## Migrating from another UI kit
|
|
2401
|
+
|
|
2402
|
+
Names and parts from other libraries and where they go.
|
|
2403
|
+
|
|
2404
|
+
| Other kits | Memos UI |
|
|
2405
|
+
| --- | --- |
|
|
2406
|
+
| Modal, Dialog | [Dialog](components/dialog.md); `sheet` covers phones |
|
|
2407
|
+
| ConfirmDialog, AlertDialog, `window.confirm` | [AlertDialog](components/dialog.md) |
|
|
2408
|
+
| Drawer, Sheet | Dialog (a bottom sheet below 640px). There is no side drawer; a narrow sidebar below `breakpoint-md` is the [page pattern](patterns.md#page) |
|
|
2409
|
+
| DropdownMenu, ActionMenu | [Menu](components/menu.md) |
|
|
2410
|
+
| ContextMenu | Not built. Put the same actions in a Menu on a visible trigger |
|
|
2411
|
+
| HoverCard | [Popover](components/popover.md) with `openOnHover` on the trigger |
|
|
2412
|
+
| Tooltip with a `title` or `content` prop | [Tooltip](components/tooltip.md) parts: Root, Trigger, Popup |
|
|
2413
|
+
| Snackbar, Toaster, sonner's `toast()` | [Toast](components/toast.md) and `Toast.useToastManager().add()` |
|
|
2414
|
+
| Toast `variant="destructive"` | `type: "danger"` |
|
|
2415
|
+
| Card, Paper, Panel | No component: spacing, a section title and [Separator](components/separator.md) |
|
|
2416
|
+
| Divider | [Separator](components/separator.md) |
|
|
2417
|
+
| Chip, Tag | [Badge](components/badge.md) to show, [Toggle](components/toggle.md) to filter |
|
|
2418
|
+
| SegmentedControl, RadioButtons in a row | [ToggleGroup](components/toggle-group.md) without `multiple` |
|
|
2419
|
+
| Accordion | [Collapsible](components/collapsible.md)s |
|
|
2420
|
+
| Slider, NumberInput, Stepper | Input `type="number"` or a Select |
|
|
2421
|
+
| Autocomplete, SearchSelect | [Combobox](components/combobox.md) |
|
|
2422
|
+
| NativeSelect, `<select>` | [Select](components/select.md) |
|
|
2423
|
+
| Label, FormLabel | `Field.Label` |
|
|
2424
|
+
| FormMessage, HelperText | `Field.Error`, `Field.Description` |
|
|
2425
|
+
| FormItem, FormControl | `Field.Root` |
|
|
2426
|
+
| Loader, CircularProgress | [Spinner](components/spinner.md), or a control's `loading` |
|
|
2427
|
+
| Progress bar | Not built. Use Skeletons for loading, or text ("3 of 10") |
|
|
2428
|
+
| Typography, Text, Heading | The type classes (`mu-body`, `mu-caption`, `mu-heading`) from [Typography](typography.md) |
|
|
2429
|
+
| Markdown or rich-text body | [Prose](components/prose.md) |
|
|
2430
|
+
| `variant="secondary"`, `"outline"` on a button | `neutral`; `ghost` in toolbars |
|
|
2431
|
+
| `variant="destructive"` on a button | `danger`, only inside a confirmation |
|
|
2432
|
+
| `size="lg"` | `md`; emphasis comes from `primary` |
|
|
2433
|
+
| `options={[…]}`, `items={[…]}` data props | Parts: `Select.Item`, `Menu.Item`, `Tabs.Tab`. Select and Combobox still take `items` on the Root for labels and filtering |
|
|
2434
|
+
| `asChild` | `render={<… />}` |
|
|
2435
|
+
|
|
2058
2436
|
<!-- docs/patterns.md -->
|
|
2059
2437
|
|
|
2060
2438
|
# Patterns
|
package/llms.txt
CHANGED
|
@@ -15,6 +15,7 @@ React components on Base UI, CSS tokens and foundation styles. Load `@usememos/u
|
|
|
15
15
|
- [States](https://github.com/usememos/ui/blob/main/docs/states.md): Hover, pressed, loading, disabled, invalid and focus
|
|
16
16
|
- [Icons](https://github.com/usememos/ui/blob/main/docs/icons.md): Lucide, sizes, fixed stroke widths and accessibility
|
|
17
17
|
- [Components](https://github.com/usememos/ui/blob/main/docs/components/README.md): The 28 components, what was left out, and one page per component
|
|
18
|
+
- [Using the components](https://github.com/usememos/ui/blob/main/docs/usage.md): Which component to pick (overlays, controls, feedback), recipes for combining them, size, emphasis, an accessibility checklist, common mistakes and a map from other UI kits
|
|
18
19
|
- [Patterns](https://github.com/usememos/ui/blob/main/docs/patterns.md): Page, form, entry, confirmation, loading, empty state and mobile
|
|
19
20
|
- [Building on it](https://github.com/usememos/ui/blob/main/docs/building.md): Base UI, loading the CSS, scoping inside a host page, theme selection and API conventions
|
|
20
21
|
- [Building your own component](https://github.com/usememos/ui/blob/main/docs/custom-components.md): How an app builds a part the package doesn't ship: Base UI, class functions, public classes, row variables and Tailwind CSS
|