material-plus-ui 1.3.1 → 1.4.0
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.
- package/README.md +2 -2
- package/dist/components/alert/MPAlert.d.ts +26 -0
- package/dist/components/alert/MPAlert.js +1 -1
- package/dist/components/alert/index.d.ts +1 -1
- package/dist/components/animate-marquee/MPAnimateMarquee.js +1 -1
- package/dist/components/avatar/MPAvatar.js +1 -1
- package/dist/components/avatar-group/MPAvatarGroup.d.ts +74 -0
- package/dist/components/avatar-group/MPAvatarGroup.js +2 -0
- package/dist/components/avatar-group/index.d.ts +2 -0
- package/dist/components/avatar-group/index.js +1 -0
- package/dist/components/breadcrumb/MPBreadcrumb.d.ts +10 -4
- package/dist/components/breadcrumb/MPBreadcrumb.js +1 -1
- package/dist/components/button/MPButton.js +1 -1
- package/dist/components/button-group/MPButtonGroup.d.ts +9 -0
- package/dist/components/button-group/MPButtonGroup.js +1 -1
- package/dist/components/carousel/MPCarousel.d.ts +12 -7
- package/dist/components/carousel/MPCarousel.js +1 -1
- package/dist/components/chat-bubble/MPChatBubble.d.ts +15 -2
- package/dist/components/chat-bubble/MPChatBubble.js +1 -1
- package/dist/components/color-picker/MPColorPicker.d.ts +20 -17
- package/dist/components/color-picker/MPColorPicker.js +1 -1
- package/dist/components/combobox/MPCombobox.d.ts +22 -4
- package/dist/components/combobox/MPCombobox.js +1 -1
- package/dist/components/command-palette/MPCommandPalette.d.ts +132 -0
- package/dist/components/command-palette/MPCommandPalette.js +2 -0
- package/dist/components/command-palette/index.d.ts +2 -0
- package/dist/components/command-palette/index.js +1 -0
- package/dist/components/container/MPContainer.js +1 -1
- package/dist/components/date-time-picker/MPDateTimePicker.d.ts +5 -0
- package/dist/components/dialog/MPDialog.js +1 -1
- package/dist/components/divider/MPDivider.js +1 -1
- package/dist/components/drawer/MPDrawer.js +1 -1
- package/dist/components/fieldset/MPFieldset.d.ts +52 -0
- package/dist/components/fieldset/MPFieldset.js +2 -0
- package/dist/components/fieldset/index.d.ts +2 -0
- package/dist/components/fieldset/index.js +1 -0
- package/dist/components/file-picker/MPFilePicker.d.ts +4 -3
- package/dist/components/file-picker/MPFilePicker.js +1 -1
- package/dist/components/floating-action-button/MPFloatingActionButton.js +1 -1
- package/dist/components/footer/MPFooter.d.ts +94 -0
- package/dist/components/footer/MPFooter.js +2 -0
- package/dist/components/footer/index.d.ts +2 -0
- package/dist/components/footer/index.js +1 -0
- package/dist/components/form/MPForm.d.ts +73 -0
- package/dist/components/form/MPForm.js +2 -0
- package/dist/components/form/index.d.ts +2 -0
- package/dist/components/form/index.js +1 -0
- package/dist/components/header/MPHeader.d.ts +122 -0
- package/dist/components/header/MPHeader.js +2 -0
- package/dist/components/header/index.d.ts +2 -0
- package/dist/components/header/index.js +1 -0
- package/dist/components/highlight/MPHighlight.d.ts +7 -0
- package/dist/components/menu/MPMenu.js +1 -1
- package/dist/components/menubar/MPMenubar.d.ts +82 -0
- package/dist/components/menubar/MPMenubar.js +2 -0
- package/dist/components/menubar/index.d.ts +2 -0
- package/dist/components/menubar/index.js +1 -0
- package/dist/components/navigation-menu/MPNavigationMenu.d.ts +151 -0
- package/dist/components/navigation-menu/MPNavigationMenu.js +2 -0
- package/dist/components/navigation-menu/index.d.ts +2 -0
- package/dist/components/navigation-menu/index.js +1 -0
- package/dist/components/number-field/MPNumberField.d.ts +12 -3
- package/dist/components/number-field/MPNumberField.js +1 -1
- package/dist/components/overlay/MPOverlay.d.ts +8 -2
- package/dist/components/overlay/MPOverlay.js +1 -1
- package/dist/components/page-layout/MPPageLayout.d.ts +145 -0
- package/dist/components/page-layout/MPPageLayout.js +2 -0
- package/dist/components/page-layout/index.d.ts +2 -0
- package/dist/components/page-layout/index.js +1 -0
- package/dist/components/panes/MPPanes.js +1 -1
- package/dist/components/popover/MPPopover.js +1 -1
- package/dist/components/progress-box/MPProgressBox.js +1 -1
- package/dist/components/progress-circular/MPProgressCircular.js +1 -1
- package/dist/components/progress-linear/MPProgressLinear.js +1 -1
- package/dist/components/radio-group/MPRadioGroup.js +1 -1
- package/dist/components/segmented-button/MPSegmentedButton.js +1 -1
- package/dist/components/select/MPSelect.d.ts +12 -1
- package/dist/components/select/MPSelect.js +1 -1
- package/dist/components/shortcut/MPShortcut.js +1 -1
- package/dist/components/sidebar/MPSidebar.d.ts +160 -0
- package/dist/components/sidebar/MPSidebar.js +2 -0
- package/dist/components/sidebar/MPSidebarTrigger.d.ts +45 -0
- package/dist/components/sidebar/MPSidebarTrigger.js +2 -0
- package/dist/components/sidebar/index.d.ts +4 -0
- package/dist/components/sidebar/index.js +1 -0
- package/dist/components/skeleton/MPSkeleton.js +1 -1
- package/dist/components/slider/MPSlider.js +1 -1
- package/dist/components/snackbar/MPSnackbar.js +1 -1
- package/dist/components/switch/MPSwitch.js +1 -1
- package/dist/components/table/MPTable.d.ts +27 -5
- package/dist/components/table/MPTable.js +1 -1
- package/dist/components/text-field/MPTextField.d.ts +13 -2
- package/dist/components/text-field/MPTextField.js +1 -1
- package/dist/components/text-link/MPTextLink.d.ts +7 -1
- package/dist/components/text-link/MPTextLink.js +1 -1
- package/dist/components/time-picker/MPTimePicker.d.ts +4 -0
- package/dist/components/toggle/MPToggle.d.ts +72 -0
- package/dist/components/toggle/MPToggle.js +2 -0
- package/dist/components/toggle/index.d.ts +2 -0
- package/dist/components/toggle/index.js +1 -0
- package/dist/components/toggle-group/MPToggleGroup.d.ts +74 -0
- package/dist/components/toggle-group/MPToggleGroup.js +2 -0
- package/dist/components/toggle-group/index.d.ts +2 -0
- package/dist/components/toggle-group/index.js +1 -0
- package/dist/components/tooltip/MPTooltip.js +1 -1
- package/dist/components/transfer/MPTransfer.d.ts +98 -0
- package/dist/components/transfer/MPTransfer.js +2 -0
- package/dist/components/transfer/index.d.ts +2 -0
- package/dist/components/transfer/index.js +1 -0
- package/dist/constants/glyphs.d.ts +10 -0
- package/dist/constants/glyphs.js +1 -1
- package/dist/constants/icons.d.ts +1 -0
- package/dist/constants/icons.js +1 -1
- package/dist/index.d.ts +13 -0
- package/dist/index.js +1 -1
- package/dist/internal/Calendar.js +1 -1
- package/dist/internal/FieldOutline.js +1 -1
- package/dist/internal/SupportingText.d.ts +1 -1
- package/dist/internal/SupportingText.js +1 -1
- package/dist/internal/animate.d.ts +0 -17
- package/dist/internal/animate.js +1 -1
- package/dist/internal/avatar-group.d.ts +22 -0
- package/dist/internal/avatar-group.js +2 -0
- package/dist/internal/button-group.d.ts +20 -1
- package/dist/internal/button-group.js +1 -1
- package/dist/internal/color.d.ts +0 -5
- package/dist/internal/color.js +1 -1
- package/dist/internal/date.js +1 -1
- package/dist/internal/i18n.d.ts +201 -4
- package/dist/internal/keys.d.ts +72 -0
- package/dist/internal/keys.js +2 -0
- package/dist/internal/length.d.ts +42 -0
- package/dist/internal/length.js +1 -0
- package/dist/internal/link.d.ts +44 -0
- package/dist/internal/link.js +1 -0
- package/dist/internal/locale.d.ts +20 -1
- package/dist/internal/locale.js +1 -1
- package/dist/internal/messages/breadcrumb.d.ts +3 -0
- package/dist/internal/messages/breadcrumb.js +1 -0
- package/dist/internal/messages/carousel.d.ts +3 -0
- package/dist/internal/messages/carousel.js +1 -0
- package/dist/internal/messages/color-picker.d.ts +3 -0
- package/dist/internal/messages/color-picker.js +1 -0
- package/dist/internal/messages/combobox.d.ts +3 -0
- package/dist/internal/messages/combobox.js +1 -0
- package/dist/internal/messages/command.d.ts +3 -0
- package/dist/internal/messages/command.js +1 -0
- package/dist/internal/messages/file-picker.d.ts +3 -0
- package/dist/internal/messages/file-picker.js +1 -0
- package/dist/internal/messages/layout.d.ts +3 -0
- package/dist/internal/messages/layout.js +1 -0
- package/dist/internal/messages/number-field.d.ts +3 -0
- package/dist/internal/messages/number-field.js +1 -0
- package/dist/internal/messages/overlay.d.ts +3 -0
- package/dist/internal/messages/overlay.js +1 -0
- package/dist/internal/messages/table.d.ts +3 -0
- package/dist/internal/messages/table.js +1 -0
- package/dist/internal/messages/text-link.d.ts +3 -0
- package/dist/internal/messages/text-link.js +1 -0
- package/dist/internal/messages/transfer.d.ts +3 -0
- package/dist/internal/messages/transfer.js +1 -0
- package/dist/internal/page-layout.d.ts +182 -0
- package/dist/internal/page-layout.js +2 -0
- package/dist/internal/scale.d.ts +17 -0
- package/dist/internal/scale.js +1 -1
- package/dist/locales/ar.js +1 -1
- package/dist/locales/de.js +1 -1
- package/dist/locales/es.js +1 -1
- package/dist/locales/fr.js +1 -1
- package/dist/locales/hi.js +1 -1
- package/dist/locales/id.js +1 -1
- package/dist/locales/it.js +1 -1
- package/dist/locales/ja.js +1 -1
- package/dist/locales/ko.js +1 -1
- package/dist/locales/nl.js +1 -1
- package/dist/locales/pl.js +1 -1
- package/dist/locales/pt.js +1 -1
- package/dist/locales/ru.js +1 -1
- package/dist/locales/th.js +1 -1
- package/dist/locales/tr.js +1 -1
- package/dist/locales/vi.js +1 -1
- package/dist/locales/zh-hans.js +1 -1
- package/dist/locales/zh-hant.js +1 -1
- package/dist/styles/accordion.css +1 -1
- package/dist/styles/alert.css +1 -1
- package/dist/styles/avatar-group.css +2 -0
- package/dist/styles/avatar.css +1 -1
- package/dist/styles/badge.css +1 -1
- package/dist/styles/blockquote.css +1 -1
- package/dist/styles/bottom-navigation.css +1 -1
- package/dist/styles/box.css +1 -1
- package/dist/styles/breadcrumb.css +1 -1
- package/dist/styles/button-group.css +1 -1
- package/dist/styles/button.css +1 -1
- package/dist/styles/card.css +1 -1
- package/dist/styles/carousel.css +1 -1
- package/dist/styles/chat-bubble.css +1 -1
- package/dist/styles/checkbox.css +1 -1
- package/dist/styles/chip.css +1 -1
- package/dist/styles/collapsible.css +1 -1
- package/dist/styles/color-picker.css +1 -1
- package/dist/styles/combobox.css +1 -1
- package/dist/styles/command-palette.css +2 -0
- package/dist/styles/date-picker.css +1 -1
- package/dist/styles/date-range-picker.css +1 -1
- package/dist/styles/date-time-picker.css +1 -1
- package/dist/styles/dialog.css +1 -1
- package/dist/styles/divider.css +1 -1
- package/dist/styles/drawer.css +1 -1
- package/dist/styles/empty.css +1 -1
- package/dist/styles/fieldset.css +2 -0
- package/dist/styles/file-picker.css +1 -1
- package/dist/styles/floating-action-button.css +1 -1
- package/dist/styles/footer.css +2 -0
- package/dist/styles/form.css +2 -0
- package/dist/styles/header.css +2 -0
- package/dist/styles/highlight.css +1 -1
- package/dist/styles/icon-button.css +1 -1
- package/dist/styles/list.css +1 -1
- package/dist/styles/locale.css +1 -1
- package/dist/styles/menu.css +1 -1
- package/dist/styles/menubar.css +2 -0
- package/dist/styles/navigation-menu.css +2 -0
- package/dist/styles/number-field.css +1 -1
- package/dist/styles/otp-field.css +1 -1
- package/dist/styles/overlay.css +1 -1
- package/dist/styles/page-layout.css +2 -0
- package/dist/styles/pagination.css +1 -1
- package/dist/styles/panes.css +1 -1
- package/dist/styles/pill.css +1 -1
- package/dist/styles/popover.css +1 -1
- package/dist/styles/progress-box.css +1 -1
- package/dist/styles/progress-circular.css +1 -1
- package/dist/styles/progress-linear.css +1 -1
- package/dist/styles/radio-group.css +1 -1
- package/dist/styles/segmented-button.css +1 -1
- package/dist/styles/select.css +1 -1
- package/dist/styles/sidebar.css +2 -0
- package/dist/styles/skeleton.css +1 -1
- package/dist/styles/slider.css +1 -1
- package/dist/styles/spoiler.css +1 -1
- package/dist/styles/switch.css +1 -1
- package/dist/styles/table.css +1 -1
- package/dist/styles/tabs.css +1 -1
- package/dist/styles/text-field.css +1 -1
- package/dist/styles/text-link.css +1 -1
- package/dist/styles/time-picker.css +1 -1
- package/dist/styles/timeline.css +1 -1
- package/dist/styles/toggle-group.css +2 -0
- package/dist/styles/toggle.css +2 -0
- package/dist/styles/transfer.css +2 -0
- package/dist/styles.css +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { MPSize } from '../../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* When a field decides whether it is valid.
|
|
5
|
+
*
|
|
6
|
+
* Base UI's own three, under Base UI's own spelling. This is one of the few
|
|
7
|
+
* places the library does *not* invent a word of its own: `validationMode` is
|
|
8
|
+
* what a caller will read everywhere else, and a nicer synonym would only be a
|
|
9
|
+
* word they had to translate back.
|
|
10
|
+
*/
|
|
11
|
+
export type MPFormValidationMode = 'onSubmit' | 'onBlur' | 'onChange';
|
|
12
|
+
/** Errors that came from somewhere else, keyed by the field's `name`. */
|
|
13
|
+
export type MPFormErrors = Record<string, string | string[]>;
|
|
14
|
+
export interface MPFormProps extends Omit<React.ComponentPropsWithoutRef<'form'>, 'onSubmit'> {
|
|
15
|
+
/**
|
|
16
|
+
* When a field validates.
|
|
17
|
+
*
|
|
18
|
+
* - `onSubmit` — on submit, and on every change afterwards. The default, and
|
|
19
|
+
* the only one that does not tell somebody their email address is wrong
|
|
20
|
+
* while they are still typing it.
|
|
21
|
+
* - `onBlur` — when a field loses focus.
|
|
22
|
+
* - `onChange` — on every keystroke.
|
|
23
|
+
* @default 'onSubmit'
|
|
24
|
+
*/
|
|
25
|
+
validationMode?: MPFormValidationMode;
|
|
26
|
+
/**
|
|
27
|
+
* Errors from outside the browser's own validation — a server, a form action,
|
|
28
|
+
* a schema — keyed by the `name` of the field each belongs to.
|
|
29
|
+
*
|
|
30
|
+
* They are shown **on the field**, and cleared as soon as that field changes.
|
|
31
|
+
*/
|
|
32
|
+
errors?: MPFormErrors;
|
|
33
|
+
/**
|
|
34
|
+
* Called on a valid submit, with the form's values. The native submit event is
|
|
35
|
+
* prevented, so nothing navigates.
|
|
36
|
+
*/
|
|
37
|
+
onSubmit?: (values: Record<string, unknown>) => void;
|
|
38
|
+
/**
|
|
39
|
+
* The gap between the form's children. A form is a stack, and this is which
|
|
40
|
+
* rung of the [ladder](../../design/prop-conventions#size) it stacks on.
|
|
41
|
+
* @default 'md'
|
|
42
|
+
*/
|
|
43
|
+
size?: MPSize;
|
|
44
|
+
children?: React.ReactNode;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A `<form>` that knows which of its fields is wrong.
|
|
48
|
+
*
|
|
49
|
+
* On its own, a page of [MPTextField](./text-field)s validates one field at a
|
|
50
|
+
* time, and a failed submit leaves the reader to go and find the red one. What
|
|
51
|
+
* this adds is the part that has to be owned **above** the fields: a submit
|
|
52
|
+
* collects every field's validity at once, focus lands on the first one that
|
|
53
|
+
* failed, and `errors` puts a server's answer back on the field it belongs to
|
|
54
|
+
* rather than in a banner at the top of the page.
|
|
55
|
+
*
|
|
56
|
+
* ## It is not a form library
|
|
57
|
+
*
|
|
58
|
+
* There is no schema here, no resolver and no field array. A project that wants
|
|
59
|
+
* those keeps the one it already has and hands the result to `errors`, which is
|
|
60
|
+
* the seam this is built around — and it is a seam rather than an integration
|
|
61
|
+
* precisely because every one of those libraries can produce
|
|
62
|
+
* `{ [name]: message }`.
|
|
63
|
+
*
|
|
64
|
+
* ## Why it lays its children out at all
|
|
65
|
+
*
|
|
66
|
+
* Because a form is a **stack**, and a stack with no gap is a stack of fields
|
|
67
|
+
* touching. The gap is the only visual decision here: no surface, no padding, no
|
|
68
|
+
* measure. Those belong to an [MPCard](../layout/card) or an
|
|
69
|
+
* [MPContainer](../layout/container) around it, for the reason a container gives
|
|
70
|
+
* — the thing that decides the shape of a page should not also be the thing that
|
|
71
|
+
* submits it.
|
|
72
|
+
*/
|
|
73
|
+
export declare const MPForm: React.ForwardRefExoticComponent<MPFormProps & React.RefAttributes<HTMLFormElement>>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import{jsx as r}from"react/jsx-runtime";import*as o from"react";import{Form as e}from"@base-ui/react/form";import{SHEET_GAP as i}from"../../internal/scale.js";export const MPForm=/*@__PURE__*/o.forwardRef(function({validationMode:o="onSubmit",errors:m,onSubmit:a,size:t="md",className:n,children:s,...f},l){return r(e,{ref:l,validationMode:o,errors:m,onFormSubmit:r=>a?.(r),"data-mp-size":t,className:["mp-form flex flex-col",i[t],n??""].filter(Boolean).join(" "),...f,children:s})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{MPForm}from"./MPForm.js";
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { useRender } from '@base-ui/react/use-render';
|
|
3
|
+
import type { MPAlign, MPPosition, MPSize, MPVariant } from '../../types.js';
|
|
4
|
+
export interface MPHeaderProps extends Omit<React.ComponentPropsWithoutRef<'header'>, 'title'> {
|
|
5
|
+
/**
|
|
6
|
+
* The leading slot: the mark, the product's name, the thing that is the same
|
|
7
|
+
* on every page — and, on a narrow window, the
|
|
8
|
+
* [MPSidebarTrigger](./sidebar#mpsidebartrigger) that goes ahead of it.
|
|
9
|
+
*
|
|
10
|
+
* A slot rather than the first of `children` because the three regions of a
|
|
11
|
+
* bar are laid out *against each other* — the middle can only be centred on
|
|
12
|
+
* the bar if the two ends are the component's to measure — and a caller
|
|
13
|
+
* writing three wrappers by hand is a caller whose header drifts from the next
|
|
14
|
+
* one.
|
|
15
|
+
*/
|
|
16
|
+
brand?: React.ReactNode;
|
|
17
|
+
/**
|
|
18
|
+
* The trailing slot: the account menu, the theme switch, the call to action.
|
|
19
|
+
* Laid out end-aligned, so a row of icon buttons needs no wrapper of its own.
|
|
20
|
+
*/
|
|
21
|
+
actions?: React.ReactNode;
|
|
22
|
+
/**
|
|
23
|
+
* Where the middle slot sits.
|
|
24
|
+
*
|
|
25
|
+
* `center` is MD3's own **center-aligned top app bar**, and it is centred on
|
|
26
|
+
* the *bar* rather than in the space left over: both ends are given equal
|
|
27
|
+
* shares, so the middle stays on the bar's midline however long the brand is.
|
|
28
|
+
* @default 'start'
|
|
29
|
+
*/
|
|
30
|
+
align?: MPAlign;
|
|
31
|
+
/**
|
|
32
|
+
* How the bar sits in the page's scroll, spelled the way CSS spells it.
|
|
33
|
+
*
|
|
34
|
+
* `sticky` — the default — holds it against the top of the window once the
|
|
35
|
+
* page has scrolled to it, while leaving it in the flow so nothing has to be
|
|
36
|
+
* padded out of its way. `fixed` takes it out of the flow entirely, which an
|
|
37
|
+
* [MPPageLayout](./page-layout) answers by reserving its height. `static` lets
|
|
38
|
+
* it scroll away.
|
|
39
|
+
* @default 'sticky'
|
|
40
|
+
*/
|
|
41
|
+
position?: MPPosition;
|
|
42
|
+
/**
|
|
43
|
+
* How much surface the bar paints, on the **container** ladder — the bar is
|
|
44
|
+
* never dyed, because what sits on it arrives with colours of its own.
|
|
45
|
+
*
|
|
46
|
+
* `tonal` is the default and is MD3's own scrolled top app bar:
|
|
47
|
+
* `surface-container` against the page's `surface`. `outlined` is the flat one
|
|
48
|
+
* — the page's own surface with a hairline where it ends — and `text` paints
|
|
49
|
+
* nothing at all, which is what a bar over a hero image wants.
|
|
50
|
+
* @default 'tonal'
|
|
51
|
+
*/
|
|
52
|
+
variant?: MPVariant;
|
|
53
|
+
/**
|
|
54
|
+
* The bar's height floor, its gutter and the air between its slots.
|
|
55
|
+
*
|
|
56
|
+
* `md` is 64px, which is MD3's own small top app bar. It is a floor rather
|
|
57
|
+
* than a height: a bar whose middle slot wrapped onto a second line would
|
|
58
|
+
* otherwise clip it.
|
|
59
|
+
* @default 'md'
|
|
60
|
+
*/
|
|
61
|
+
size?: MPSize;
|
|
62
|
+
/**
|
|
63
|
+
* Holds the row of slots to a measure and centres it while the sheet itself
|
|
64
|
+
* still spans the window. The same ladder [MPContainer](./container)'s
|
|
65
|
+
* `maxWidth` uses, so a header and the container under it line up on one edge.
|
|
66
|
+
* @default 'none'
|
|
67
|
+
*/
|
|
68
|
+
maxWidth?: MPSize | 'none';
|
|
69
|
+
/**
|
|
70
|
+
* The gutter down each side of the row.
|
|
71
|
+
* @default true
|
|
72
|
+
*/
|
|
73
|
+
padded?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* The name the bar is announced by. Worth writing when a page has more than
|
|
76
|
+
* one `<header>` in it — an article's own and the site's — because "banner"
|
|
77
|
+
* twice tells a reader which is which not at all.
|
|
78
|
+
*/
|
|
79
|
+
label?: string;
|
|
80
|
+
/**
|
|
81
|
+
* Renders something other than a `<header>`. Base UI's own escape hatch, and
|
|
82
|
+
* rarely what you want: at the top level of a document that tag *is* the
|
|
83
|
+
* `banner` landmark, and it is what says so to a screen reader's landmark
|
|
84
|
+
* list, to a reader mode and to a crawler.
|
|
85
|
+
*/
|
|
86
|
+
render?: useRender.RenderProp;
|
|
87
|
+
/** The middle slot: a row of navigation links, a search field, a headline. */
|
|
88
|
+
children?: React.ReactNode;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The bar across the top of a page. **MD3's top app bar**, with the site's own
|
|
92
|
+
* three regions on it.
|
|
93
|
+
*
|
|
94
|
+
* A real `<header>`, which is the whole reason it is a component rather than a
|
|
95
|
+
* row of divs: at the top level of a document that tag is the `banner` landmark,
|
|
96
|
+
* and it is what a screen reader's landmark list, a reader mode and a search
|
|
97
|
+
* engine's understanding of the page are built out of.
|
|
98
|
+
*
|
|
99
|
+
* The slots are props rather than compound sub-components, for
|
|
100
|
+
* [MPCard](./card)'s reason: the arrangement is fixed — brand, middle, actions —
|
|
101
|
+
* and what a caller wants to decide is what goes in each. That the middle can be
|
|
102
|
+
* centred on the bar's own midline is only possible *because* the ends are the
|
|
103
|
+
* component's to measure.
|
|
104
|
+
*
|
|
105
|
+
* ## Why there is no `divider`
|
|
106
|
+
*
|
|
107
|
+
* [MPBottomNavigation](./bottom-navigation) has one and this does not, and the
|
|
108
|
+
* difference is that this takes a `variant`. MD3 separates a bar from the
|
|
109
|
+
* content by **tone** — `surface-container` against `surface` — and the case a
|
|
110
|
+
* hairline exists for is the bar that paints the page's own surface. That bar is
|
|
111
|
+
* `variant="outlined"`, and the rule is what `outlined` *means* here: a
|
|
112
|
+
* container's outline is a hairline all the way round, and a bar has exactly one
|
|
113
|
+
* edge with anything on the other side of it.
|
|
114
|
+
*
|
|
115
|
+
* ## Inside a layout, and outside one
|
|
116
|
+
*
|
|
117
|
+
* Inside an [MPPageLayout](./page-layout) the bar registers itself, so a column
|
|
118
|
+
* that holds its place knows how far down the window to start and a `fixed` bar
|
|
119
|
+
* has its height reserved. Outside one it is simply a bar, and everything above
|
|
120
|
+
* still holds.
|
|
121
|
+
*/
|
|
122
|
+
export declare const MPHeader: React.ForwardRefExoticComponent<MPHeaderProps & React.RefAttributes<HTMLElement>>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as a from"react";import{useRender as i}from"@base-ui/react/use-render";import{BAR_SURFACE as n,MPPageLayoutContext as r}from"../../internal/page-layout.js";import{hasContent as s,MEASURE as l,SHEET_PAD_X as m}from"../../internal/scale.js";const d={xs:"min-h-12",sm:"min-h-14",md:"min-h-16",lg:"min-h-20",xl:"min-h-24"},o={xs:"text-mp-title-small",sm:"text-mp-title-medium",md:"text-mp-title-large",lg:"text-mp-headline-small",xl:"text-mp-headline-medium"},c={xs:"gap-1.5",sm:"gap-2",md:"gap-3",lg:"gap-4",xl:"gap-5"},p={xs:"gap-3",sm:"gap-4",md:"gap-6",lg:"gap-8",xl:"gap-10"},x={static:"",absolute:"absolute inset-x-0 top-0 z-30",sticky:"sticky top-0 z-30",fixed:"fixed inset-x-0 top-0 z-40"},f={start:"shrink-0",center:"flex-1 basis-0",end:"shrink-0"},u={start:"flex min-w-0 flex-1 items-center justify-start",center:"flex min-w-0 shrink items-center justify-center",end:"flex min-w-0 flex-1 items-center justify-end"};export const MPHeader=/*@__PURE__*/a.forwardRef(function({brand:h,actions:g,align:b="start",position:j="sticky",variant:v="tonal",size:w="md",maxWidth:y="none",padded:N=!0,label:k,render:_,className:z,children:$,...B},C){const{register:H}=a.useContext(r),M=a.useCallback(e=>{H("header",e),"function"==typeof C?C(e):C&&(C.current=e)},[H,C]);return i({render:_??e("header",{}),ref:M,props:{"aria-label":k,"data-mp-size":w,"data-mp-variant":v,className:["mp-header text-mp-on-surface box-border w-full min-w-0",n[v],"outlined"===v?"border-mp-outline-variant border-b":"",x[j],z??""].filter(Boolean).join(" "),children:t("div",{className:["flex w-full items-center",d[w],p[w],N?m[w]:"","none"===y?"":`${l[y]} mx-auto`].filter(Boolean).join(" "),children:[s(h)?e("div",{className:["mp-header__brand flex min-w-0 items-center",f[b],c[w],o[w]].join(" "),children:h}):"center"===b?e("div",{"aria-hidden":"true",className:f[b]}):null,s($)?e("div",{className:`mp-header__middle ${u[b]} ${c[w]}`,children:$}):null,s(g)?e("div",{className:["mp-header__actions flex min-w-0 items-center justify-end",f[b],c[w]].join(" "),children:g}):"center"===b?e("div",{"aria-hidden":"true",className:f[b]}):null]}),...B}})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{MPHeader}from"./MPHeader.js";
|
|
@@ -19,6 +19,13 @@ export interface MPHighlightProps extends Omit<React.ComponentPropsWithoutRef<'s
|
|
|
19
19
|
* letters of it. A `RegExp` is used as written, with the global flag forced on;
|
|
20
20
|
* `caseSensitive` and `wholeWord` are ignored for it, because a regular
|
|
21
21
|
* expression already says both of those things itself.
|
|
22
|
+
*
|
|
23
|
+
* Strings are escaped, so a search box's contents are safe to pass straight
|
|
24
|
+
* through however they are spelled. A `RegExp` is **not** — it is run as
|
|
25
|
+
* written, which is the point of accepting one. Building one out of text
|
|
26
|
+
* somebody else typed is a denial of service waiting for the input that
|
|
27
|
+
* backtracks; escape it into a string term instead, or keep the expression a
|
|
28
|
+
* literal in your own source.
|
|
22
29
|
*/
|
|
23
30
|
query: string | string[] | RegExp;
|
|
24
31
|
/**
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import{jsx as e,jsxs as n}from"react/jsx-runtime";import*as t from"react";import{Menu as a}from"@base-ui/react/menu";import{ContextMenu as l}from"@base-ui/react/context-menu";import{MPIcon as r}from"../icon/MPIcon.js";import{CheckIcon as
|
|
2
|
+
import{jsx as e,jsxs as n}from"react/jsx-runtime";import*as t from"react";import{Menu as a}from"@base-ui/react/menu";import{ContextMenu as l}from"@base-ui/react/context-menu";import{MPIcon as r}from"../icon/MPIcon.js";import{CheckIcon as s,ChevronRightIcon as o}from"../../constants/icons.js";import{linkRel as i}from"../../internal/link.js";import{MPMenuContext as c}from"../../internal/menu.js";import{accentSlots as d}from"../../internal/accent.js";import{MPStateLayer as m}from"../../internal/StateLayer.js";import{CONTROL_ICON as u,META_TEXT as p,hasContent as h}from"../../internal/scale.js";import{FADE as f,PORTAL_LAYER as x}from"../../internal/surface.js";const b=/*@__PURE__*/["mp-menu__popup rounded-mp-xs shadow-mp-2 bg-mp-surface-container text-mp-on-surface","max-h-[min(24rem,var(--available-height))] min-w-[112px] max-w-[280px]","overflow-y-auto overscroll-contain py-2 outline-none",f].join(" "),g={xs:"min-h-8",sm:"min-h-10",md:"min-h-12",lg:"min-h-14",xl:"min-h-16"},N={xs:"px-2",sm:"px-2.5",md:"px-3",lg:"px-4",xl:"px-5"},v={xs:"gap-2",sm:"gap-2.5",md:"gap-3",lg:"gap-3",xl:"gap-4"},C={xs:"text-mp-label-medium",sm:"text-mp-label-large",md:"text-mp-label-large",lg:"text-mp-title-medium",xl:"text-mp-title-medium"};function y(e,n,t){return["group relative flex w-full cursor-pointer items-center select-none",n?"text-(--_mp-accent)":"text-mp-on-surface",g[e],N[e],v[e],C[e],"data-disabled:text-mp-on-surface/38 data-disabled:cursor-default","outline-none",t??""].filter(Boolean).join(" ")}function k(){return e(m,{layer:"inset-0 bg-current",className:"group-data-highlighted:opacity-8 group-data-popup-open:opacity-8 group-data-disabled:opacity-0"})}const M="relative flex shrink-0 items-center justify-center";function O({children:t,description:a}){return h(a)?n("span",{className:"relative flex min-w-0 flex-1 flex-col py-1.5 text-start",children:[e("span",{className:"truncate",children:t}),e("span",{className:`text-mp-on-surface-variant truncate ${p}`,children:a})]}):e("span",{className:"relative min-w-0 flex-1 truncate text-start",children:t})}function P({shortcut:n}){return h(n)?e("span",{className:`text-mp-on-surface-variant relative ms-2 shrink-0 ${p}`,children:n}):null}export function MPMenuItem({onClick:l,href:r,target:s,rel:o,startIcon:d,endIcon:m,shortcut:u,description:p,color:f,closeOnClick:x=!0,disabled:b=!1,label:g,children:N,className:v,style:C}){const{size:j}=t.useContext(c),I=n(t.Fragment,{children:[e(k,{}),h(d)?e("span",{className:`${M} text-mp-on-surface-variant`,children:d}):null,e(O,{description:p,children:N}),h(m)?e("span",{className:`${M} text-mp-on-surface-variant`,children:m}):null,e(P,{shortcut:u})]}),$=f?{"--_mp-accent":`var(--_mp-color-${f})`}:void 0,z=$||C?{...$,...C}:void 0;return void 0===r||b?e(a.Item,{disabled:b,label:g,closeOnClick:x,onClick:l,className:y(j,Boolean(f),v),style:z,children:I}):e(a.LinkItem,{href:r,target:s,rel:i(s,o),label:g,closeOnClick:x,onClick:l,className:y(j,Boolean(f),v),style:z,children:I})}export function MPMenuCheckboxItem({checked:l,defaultChecked:o,onCheckedChange:i,endIcon:d,shortcut:m,description:p,color:f,closeOnClick:x=!1,disabled:b=!1,label:g,children:N,className:v,style:C}){const{size:j}=t.useContext(c),I=f?{"--_mp-accent":`var(--_mp-color-${f})`}:void 0;return n(a.CheckboxItem,{checked:l,defaultChecked:o,onCheckedChange:e=>i?.(e),disabled:b,label:g,closeOnClick:x,className:y(j,Boolean(f),v),style:I||C?{...I,...C}:void 0,children:[e(k,{}),e("span",{className:`${M} text-(--_mp-accent)`,style:{width:u[j]},children:e(a.CheckboxItemIndicator,{className:"flex items-center justify-center",children:e(r,{icon:s,size:u[j]})})}),e(O,{description:p,children:N}),h(d)?e("span",{className:`${M} text-mp-on-surface-variant`,children:d}):null,e(P,{shortcut:m})]})}export function MPMenuRadioGroup({value:n,defaultValue:t,onValueChange:l,disabled:r=!1,children:s,className:o}){return e(a.RadioGroup,{value:n,defaultValue:t,onValueChange:e=>l?.(e),disabled:r,className:o,children:s})}export function MPMenuRadioItem({value:l,endIcon:r,shortcut:s,description:o,color:i,closeOnClick:d=!1,disabled:m=!1,label:p,children:f,className:x,style:b}){const{size:g}=t.useContext(c),N=i?{"--_mp-accent":`var(--_mp-color-${i})`}:void 0;return n(a.RadioItem,{value:l,disabled:m,label:p,closeOnClick:d,className:y(g,Boolean(i),x),style:N||b?{...N,...b}:void 0,children:[e(k,{}),e("span",{className:`${M} text-(--_mp-accent)`,style:{width:u[g]},children:e(a.RadioItemIndicator,{className:"flex items-center justify-center",children:e("span",{className:"block size-2.5 rounded-full bg-current"})})}),e(O,{description:o,children:f}),h(r)?e("span",{className:`${M} text-mp-on-surface-variant`,children:r}):null,e(P,{shortcut:s})]})}export function MPMenuGroup({label:l,children:r,className:s}){const{size:o}=t.useContext(c);return n(a.Group,{className:s,children:[h(l)?e(a.GroupLabel,{className:`text-mp-on-surface-variant pt-2 pb-1 ${N[o]} ${p}`,children:l}):null,r]})}export function MPMenuSeparator({className:n,...t}){return e(a.Separator,{className:["bg-mp-outline-variant my-2 h-px",n??""].filter(Boolean).join(" "),...t})}export function MPMenuSubmenu({label:l,startIcon:s,disabled:i=!1,side:m="right",sideOffset:p=0,children:f,className:g,style:N}){const{size:v,color:C}=t.useContext(c);return n(a.SubmenuRoot,{children:[n(a.SubmenuTrigger,{disabled:i,className:y(v,!1),children:[e(k,{}),h(s)?e("span",{className:`${M} text-mp-on-surface-variant`,children:s}):null,e("span",{className:"relative min-w-0 flex-1 truncate text-start",children:l}),e("span",{className:`${M} text-mp-on-surface-variant rtl:rotate-180`,children:e(r,{icon:o,size:u[v]})})]}),e(a.Portal,{children:e(a.Positioner,{className:x,side:m,sideOffset:p,align:"start",children:e(a.Popup,{className:[b,g??""].filter(Boolean).join(" "),style:{...d(C),...N},children:f})})})]})}export function MPMenu({size:l="md",color:r="primary",trigger:s,open:o,defaultOpen:i,onOpenChange:m,side:u="bottom",align:p="start",sideOffset:h=4,modal:f,openOnHover:g=!1,loopFocus:N=!0,disabled:v=!1,className:C,style:y,children:k}){const M=t.useMemo(()=>({size:l,color:r}),[l,r]);return e(c.Provider,{value:M,children:n(a.Root,{open:o,defaultOpen:i,onOpenChange:e=>m?.(e),modal:f,loopFocus:N,disabled:v,children:[s?e(a.Trigger,{render:s,openOnHover:g,disabled:v}):null,e(a.Portal,{children:e(a.Positioner,{className:x,side:u,align:p,sideOffset:h,children:e(a.Popup,{"data-mp-size":l,className:[b,C??""].filter(Boolean).join(" "),style:{...d(r),...y},children:k})})})]})})}export function MPContextMenu({size:a="md",color:r="primary",content:s,children:o,open:i,defaultOpen:m,onOpenChange:u,loopFocus:p=!0,disabled:h=!1,className:f,style:g}){const N=t.useMemo(()=>({size:a,color:r}),[a,r]);return e(c.Provider,{value:N,children:n(l.Root,{open:i,defaultOpen:m,onOpenChange:e=>u?.(e),loopFocus:p,disabled:h,children:[e(l.Trigger,{children:o}),e(l.Portal,{children:e(l.Positioner,{className:x,children:e(l.Popup,{"data-mp-size":a,className:[b,f??""].filter(Boolean).join(" "),style:{...d(r),...g},children:s})})})]})})}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { MPColor, MPOrientation, MPSize } from '../../types.js';
|
|
3
|
+
export interface MPMenubarProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'color'> {
|
|
4
|
+
/**
|
|
5
|
+
* Which way the bar runs. `vertical` is the shape a side rail of menus takes;
|
|
6
|
+
* the arrow keys follow it either way.
|
|
7
|
+
* @default 'horizontal'
|
|
8
|
+
*/
|
|
9
|
+
orientation?: MPOrientation;
|
|
10
|
+
/**
|
|
11
|
+
* Whether an open menu takes the page away.
|
|
12
|
+
* @default true
|
|
13
|
+
*/
|
|
14
|
+
modal?: boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Whether the arrow keys wrap around at the ends of the bar.
|
|
17
|
+
* @default true
|
|
18
|
+
*/
|
|
19
|
+
loopFocus?: boolean;
|
|
20
|
+
/** Disables every menu on the bar at once. */
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* The row height and type scale, passed to every menu on the bar.
|
|
24
|
+
* @default 'md'
|
|
25
|
+
*/
|
|
26
|
+
size?: MPSize;
|
|
27
|
+
/**
|
|
28
|
+
* Which accent family an open menu's word reads, and which a chosen row inside
|
|
29
|
+
* it reads.
|
|
30
|
+
* @default 'primary'
|
|
31
|
+
*/
|
|
32
|
+
color?: MPColor;
|
|
33
|
+
/** The `MPMenubarMenu`s. */
|
|
34
|
+
children?: React.ReactNode;
|
|
35
|
+
}
|
|
36
|
+
export interface MPMenubarMenuProps {
|
|
37
|
+
/** The word on the bar. */
|
|
38
|
+
label: React.ReactNode;
|
|
39
|
+
/** A glyph before the label. */
|
|
40
|
+
startIcon?: React.ReactNode;
|
|
41
|
+
/** Unavailable. The word stays on the bar and opens nothing. */
|
|
42
|
+
disabled?: boolean;
|
|
43
|
+
/** The rows, written exactly as they are inside an [MPMenu](./menu). */
|
|
44
|
+
children?: React.ReactNode;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* One menu on the bar: the word, and the rows behind it.
|
|
48
|
+
*
|
|
49
|
+
* It has no `size` and no `color` of its own — both belong to the bar, which is
|
|
50
|
+
* the only place they can be set once and hold for every menu on it. The rows
|
|
51
|
+
* inside are the same [MPMenuItem](./menu), `MPMenuSeparator`, `MPMenuGroup` and
|
|
52
|
+
* `MPMenuSubmenu` an [MPMenu](./menu) takes, because it *is* the same menu.
|
|
53
|
+
*/
|
|
54
|
+
export declare function MPMenubarMenu({ label, startIcon, disabled, children }: MPMenubarMenuProps): React.JSX.Element;
|
|
55
|
+
/**
|
|
56
|
+
* The strip of words at the top of an application — File, Edit, View — each of
|
|
57
|
+
* which opens a menu.
|
|
58
|
+
*
|
|
59
|
+
* ## What makes it a bar rather than a row of menus
|
|
60
|
+
*
|
|
61
|
+
* What happens once one of them is **open**: moving along the strip walks
|
|
62
|
+
* through the others instead of closing the one you left, and the arrow keys
|
|
63
|
+
* move between the menus as well as inside them. Base UI owns all of that, along
|
|
64
|
+
* with the `menubar` role — which is what tells a screen reader that the strip
|
|
65
|
+
* is one widget with one tab stop rather than six unrelated buttons.
|
|
66
|
+
*
|
|
67
|
+
* ## Why it draws no surface
|
|
68
|
+
*
|
|
69
|
+
* A menu bar sits **on** something: an [MPHeader](../layout/header), a title
|
|
70
|
+
* bar, a toolbar. A sheet under a strip that is already on a sheet is two
|
|
71
|
+
* sheets, and the second one has nothing to say. It is the same refusal
|
|
72
|
+
* [MPContainer](../layout/container#why-it-draws-no-surface) makes, one level
|
|
73
|
+
* down.
|
|
74
|
+
*
|
|
75
|
+
* ## When this is the wrong component
|
|
76
|
+
*
|
|
77
|
+
* When there are more actions than a strip can hold. A menu bar's whole
|
|
78
|
+
* advantage is that every heading is visible before you go looking for one, and
|
|
79
|
+
* a bar of twelve words has already lost it — that is an
|
|
80
|
+
* [MPCommandPalette](./command-palette).
|
|
81
|
+
*/
|
|
82
|
+
export declare const MPMenubar: React.ForwardRefExoticComponent<MPMenubarProps & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import{jsx as e,jsxs as t}from"react/jsx-runtime";import*as o from"react";import{Menubar as r}from"@base-ui/react/menubar";import{MPMenu as n}from"../menu/MPMenu.js";import{accentSlots as i}from"../../internal/accent.js";import{MPMenuContext as s}from"../../internal/menu.js";import{MPStateLayer as a}from"../../internal/StateLayer.js";import{CONTROL_GAP as l,hasContent as m}from"../../internal/scale.js";const c={xs:"h-6",sm:"h-7",md:"h-8",lg:"h-10",xl:"h-12"},p={xs:"px-1.5",sm:"px-2",md:"px-2.5",lg:"px-3",xl:"px-4"},u={xs:"text-mp-label-medium",sm:"text-mp-label-large",md:"text-mp-label-large",lg:"text-mp-title-medium",xl:"text-mp-title-medium"};export function MPMenubarMenu({label:r,startIcon:i,disabled:d=!1,children:f}){const{size:x,color:b}=o.useContext(s);return e(n,{size:x,color:b,disabled:d,openOnHover:!0,sideOffset:4,trigger:t("button",{type:"button",disabled:d,className:["mp-menubar__menu group text-mp-on-surface relative inline-flex shrink-0","rounded-mp-xs cursor-pointer items-center justify-center bg-transparent","appearance-none font-[inherit] whitespace-nowrap select-none","transition-colors duration-(--mp-sys-motion-duration-short4)","data-popup-open:text-(--_mp-accent)","disabled:text-mp-on-surface/38 disabled:cursor-default","outline-mp-secondary focus-visible:outline-2 focus-visible:-outline-offset-2","focus-visible:outline-solid outline-none",c[x],p[x],u[x],l[x]].join(" "),children:[d?null:e(a,{className:"group-data-popup-open:opacity-8"}),m(i)?e("span",{className:"relative flex",children:i}):null,e("span",{className:"relative",children:r})]}),children:f})}export const MPMenubar=/*@__PURE__*/o.forwardRef(function({orientation:t="horizontal",modal:n=!0,loopFocus:a=!0,disabled:m=!1,size:c="md",color:p="primary",className:u,style:d,children:f,...x},b){const h=o.useMemo(()=>({size:c,color:p}),[c,p]);return e(s.Provider,{value:h,children:e(r,{ref:b,orientation:t,modal:n,loopFocus:a,disabled:m,"data-mp-size":c,"data-mp-orientation":t,className:["mp-menubar flex items-center","vertical"===t?"flex-col items-stretch":"flex-row",l[c],u??""].filter(Boolean).join(" "),style:{...i(p),...d},...x,children:f})})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{MPMenubar,MPMenubarMenu}from"./MPMenubar.js";
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type { MPColor, MPOrientation, MPSize } from '../../types.js';
|
|
3
|
+
export interface MPNavigationMenuProps extends Omit<React.ComponentPropsWithoutRef<'nav'>, 'color' | 'defaultValue' | 'onChange'> {
|
|
4
|
+
/**
|
|
5
|
+
* Which way the row runs. `vertical` is a rail whose panels open beside it
|
|
6
|
+
* rather than under it; the arrow keys follow either way.
|
|
7
|
+
* @default 'horizontal'
|
|
8
|
+
*/
|
|
9
|
+
orientation?: MPOrientation;
|
|
10
|
+
/** Which item's panel is open, by its `value`. Nullish is closed. */
|
|
11
|
+
value?: string | null;
|
|
12
|
+
/** Which starts open, for an uncontrolled menu. */
|
|
13
|
+
defaultValue?: string | null;
|
|
14
|
+
onValueChange?: (value: string | null) => void;
|
|
15
|
+
/**
|
|
16
|
+
* How long the pointer rests on an item before its panel opens, in
|
|
17
|
+
* milliseconds.
|
|
18
|
+
*/
|
|
19
|
+
delay?: number;
|
|
20
|
+
/** And how long a panel stays after the pointer leaves. */
|
|
21
|
+
closeDelay?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Distance from the row, in pixels.
|
|
24
|
+
* @default 8
|
|
25
|
+
*/
|
|
26
|
+
sideOffset?: number;
|
|
27
|
+
/**
|
|
28
|
+
* The row's height and type scale.
|
|
29
|
+
* @default 'md'
|
|
30
|
+
*/
|
|
31
|
+
size?: MPSize;
|
|
32
|
+
/**
|
|
33
|
+
* Which accent family an open item's label reads. The panel itself stays
|
|
34
|
+
* neutral, as MD3's menu surface does.
|
|
35
|
+
* @default 'primary'
|
|
36
|
+
*/
|
|
37
|
+
color?: MPColor;
|
|
38
|
+
/**
|
|
39
|
+
* The name the `<nav>` is announced by, through `aria-label` in the rest
|
|
40
|
+
* props.
|
|
41
|
+
*
|
|
42
|
+
* There is deliberately no default. A generic one would say nothing a screen
|
|
43
|
+
* reader was not already saying — it announces an unnamed `<nav>` as
|
|
44
|
+
* "navigation" on its own — so a name is only worth having when it
|
|
45
|
+
* distinguishes this bar from another region on the page.
|
|
46
|
+
*
|
|
47
|
+
* Which is exactly when it becomes necessary: a page with a main bar, a
|
|
48
|
+
* footer's links and a table of contents offers three regions called
|
|
49
|
+
* "navigation" and no way to tell them apart. Name them the moment there is
|
|
50
|
+
* more than one.
|
|
51
|
+
*/
|
|
52
|
+
children?: React.ReactNode;
|
|
53
|
+
}
|
|
54
|
+
export interface MPNavigationMenuItemProps {
|
|
55
|
+
/** The word in the row. */
|
|
56
|
+
label: React.ReactNode;
|
|
57
|
+
/**
|
|
58
|
+
* Makes the item a plain link rather than something that opens a panel.
|
|
59
|
+
*
|
|
60
|
+
* An item with an `href` and no children is a **destination**, and it is
|
|
61
|
+
* announced as one — which is the whole reason a site's navigation is not an
|
|
62
|
+
* [MPMenu](../inputs/menu).
|
|
63
|
+
*/
|
|
64
|
+
href?: string;
|
|
65
|
+
/** Where the link opens. Ignored without `href`. */
|
|
66
|
+
target?: string;
|
|
67
|
+
/**
|
|
68
|
+
* The link's relationship to the page. Ignored without `href`.
|
|
69
|
+
*
|
|
70
|
+
* Left unset, a `target="_blank"` destination gets `noopener noreferrer` —
|
|
71
|
+
* the pair that stops the opened page reaching back through `window.opener`.
|
|
72
|
+
* Setting it replaces that rather than adding to it, so a destination that
|
|
73
|
+
* also needs `nofollow` should say `rel="noopener noreferrer nofollow"`.
|
|
74
|
+
*/
|
|
75
|
+
rel?: string;
|
|
76
|
+
/** A glyph before the label. */
|
|
77
|
+
startIcon?: React.ReactNode;
|
|
78
|
+
/**
|
|
79
|
+
* Identifies the item, for a controlled menu. Base UI gives each item an
|
|
80
|
+
* identity of its own, so an uncontrolled menu needs none.
|
|
81
|
+
*/
|
|
82
|
+
value?: string;
|
|
83
|
+
/** Unavailable. The word stays in the row and opens nothing. */
|
|
84
|
+
disabled?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* How many columns the panel lays its links out in.
|
|
87
|
+
* @default 1
|
|
88
|
+
*/
|
|
89
|
+
columns?: number;
|
|
90
|
+
/** The panel's contents — usually `MPNavigationMenuLink`s. */
|
|
91
|
+
children?: React.ReactNode;
|
|
92
|
+
}
|
|
93
|
+
export interface MPNavigationMenuLinkProps extends Omit<React.ComponentPropsWithoutRef<'a'>, 'color' | 'title'> {
|
|
94
|
+
/** Where it goes. */
|
|
95
|
+
href: string;
|
|
96
|
+
/** The row's name. */
|
|
97
|
+
title: React.ReactNode;
|
|
98
|
+
/** A second line under it, one step down the scale and muted. */
|
|
99
|
+
description?: React.ReactNode;
|
|
100
|
+
/** A glyph before the title. */
|
|
101
|
+
startIcon?: React.ReactNode;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* One row inside a panel: where it goes, what it is called, and a line saying
|
|
105
|
+
* what is there.
|
|
106
|
+
*
|
|
107
|
+
* A real `<a>`, which is the point of the whole component — a site's navigation
|
|
108
|
+
* is a list of destinations, and a destination that is a `<div>` with a click
|
|
109
|
+
* handler is not in the link list, not on the status bar and not in a crawler's
|
|
110
|
+
* index.
|
|
111
|
+
*/
|
|
112
|
+
export declare const MPNavigationMenuLink: React.ForwardRefExoticComponent<MPNavigationMenuLinkProps & React.RefAttributes<HTMLAnchorElement>>;
|
|
113
|
+
/**
|
|
114
|
+
* One word in the row, and what opens under it.
|
|
115
|
+
*
|
|
116
|
+
* With children it is a trigger and a panel; with an `href` and nothing else it
|
|
117
|
+
* is a link — and the difference is not cosmetic. The second is announced as a
|
|
118
|
+
* destination and the first as something that expands.
|
|
119
|
+
*/
|
|
120
|
+
export declare function MPNavigationMenuItem({ label, href, target, rel, startIcon, value, disabled, columns, children }: MPNavigationMenuItemProps): React.JSX.Element;
|
|
121
|
+
/**
|
|
122
|
+
* A site's navigation: a row of destinations, some of which open a panel of more
|
|
123
|
+
* of them.
|
|
124
|
+
*
|
|
125
|
+
* ## Why this is not an MPMenu
|
|
126
|
+
*
|
|
127
|
+
* Because of what the rows **are**.
|
|
128
|
+
*
|
|
129
|
+
* An [MPMenu](../inputs/menu) holds actions, so its rows are `menuitem`s and the
|
|
130
|
+
* whole thing is a widget that takes the arrow keys. This holds links, so it is
|
|
131
|
+
* a `<nav>` full of real `<a>`s — which is what puts each destination in a
|
|
132
|
+
* screen reader's link list, on the browser's status bar, on the middle-click
|
|
133
|
+
* menu and in a crawler's index.
|
|
134
|
+
*
|
|
135
|
+
* The rule is short: reach for a menu when the row **does** something, and for
|
|
136
|
+
* this when the row **goes** somewhere.
|
|
137
|
+
*
|
|
138
|
+
* ## One panel, resized
|
|
139
|
+
*
|
|
140
|
+
* One panel is open at a time and it resizes between items rather than closing
|
|
141
|
+
* and reopening, which is Base UI's doing. It is also why the popup animates its
|
|
142
|
+
* width and height as well as its opacity: crossing the row should read as one
|
|
143
|
+
* surface following the pointer, not as three sheets flashing.
|
|
144
|
+
*
|
|
145
|
+
* ## Where it goes
|
|
146
|
+
*
|
|
147
|
+
* In an [MPHeader](../layout/header)'s middle slot, which is what that slot is
|
|
148
|
+
* for. Down the side of a page it is `orientation="vertical"`, which is a rail
|
|
149
|
+
* whose panels open beside it.
|
|
150
|
+
*/
|
|
151
|
+
export declare const MPNavigationMenu: React.ForwardRefExoticComponent<MPNavigationMenuProps & React.RefAttributes<HTMLElement>>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import{jsx as e,jsxs as n,Fragment as a}from"react/jsx-runtime";import*as t from"react";import{NavigationMenu as i}from"@base-ui/react/navigation-menu";import{MPIcon as r}from"../icon/MPIcon.js";import{ChevronDownIcon as o}from"../../constants/icons.js";import{accentSlots as s}from"../../internal/accent.js";import{linkRel as l}from"../../internal/link.js";import{MPStateLayer as c}from"../../internal/StateLayer.js";import{CONTROL_GAP as m,CONTROL_HEIGHT as p,CONTROL_ICON as d,CONTROL_PAD_X as u,CONTROL_TEXT as f,hasContent as h,META_TEXT as g}from"../../internal/scale.js";import{PORTAL_LAYER as x}from"../../internal/surface.js";const v=/*@__PURE__*/t.createContext({size:"md"}),N=/*@__PURE__*/["mp-navigation-menu__popup rounded-mp-xs shadow-mp-2","bg-mp-surface-container text-mp-on-surface box-border overflow-hidden outline-none","transition-[opacity,width,height] duration-(--mp-sys-motion-duration-short4)","ease-mp-standard data-starting-style:opacity-0 data-ending-style:opacity-0"].join(" "),y={xs:"p-1",sm:"p-1.5",md:"p-2",lg:"p-2.5",xl:"p-3"};export const MPNavigationMenuLink=/*@__PURE__*/t.forwardRef(function({href:a,title:r,description:o,startIcon:s,target:p,rel:d,className:x,children:N,...y},j){const{size:b}=t.useContext(v);return n(i.Link,{ref:j,href:a,target:p,rel:l(p,d),className:["mp-navigation-menu__link group text-mp-on-surface rounded-mp-xs relative flex","min-w-0 cursor-pointer items-start bg-transparent py-2 no-underline outline-none",m[b],u[b],x??""].filter(Boolean).join(" "),...y,children:[e(c,{}),h(s)?e("span",{className:"relative flex h-[1lh] shrink-0 items-center",children:s}):null,n("span",{className:"relative flex min-w-0 flex-col gap-0.5",children:[e("span",{className:`font-medium ${f[b]}`,children:r}),h(o)?e("span",{className:`text-mp-on-surface-variant ${g}`,children:o}):null,N]})]})});export function MPNavigationMenuItem({label:s,href:g,target:x,rel:N,startIcon:j,value:b,disabled:w=!1,columns:M=1,children:P}){const{size:_}=t.useContext(v),z=void 0!==g&&!h(P),k=Number.isFinite(M)?Math.max(1,Math.round(M)):1,C=/*@__PURE__*/["mp-navigation-menu__item group text-mp-on-surface relative inline-flex shrink-0","rounded-mp-full cursor-pointer items-center justify-center bg-transparent","appearance-none font-[inherit] whitespace-nowrap no-underline select-none","outline-mp-secondary focus-visible:outline-2 focus-visible:outline-offset-2","focus-visible:outline-solid outline-none","transition-colors duration-(--mp-sys-motion-duration-short4)","data-popup-open:text-(--_mp-accent)","data-disabled:text-mp-on-surface/38 data-disabled:cursor-default",p[_],f[_],m[_],u[_]].join(" ");return e(i.Item,{value:b,children:z?n(i.Link,{href:g,target:x,rel:l(x,N),className:C,children:[e(c,{}),h(j)?e("span",{className:"relative flex",children:j}):null,e("span",{className:"relative",children:s})]}):n(a,{children:[n(i.Trigger,{disabled:w,className:C,children:[w?null:e(c,{className:"group-data-popup-open:opacity-8"}),h(j)?e("span",{className:"relative flex",children:j}):null,e("span",{className:"relative",children:s}),e(i.Icon,{className:["relative flex items-center transition-transform","duration-(--mp-sys-motion-duration-short4) ease-mp-standard","data-popup-open:rotate-180"].join(" "),children:e(r,{icon:o,size:d[_]})})]}),e(i.Content,{className:`mp-navigation-menu__panel grid gap-1 ${y[_]}`,style:k>1?{gridTemplateColumns:`repeat(${k}, minmax(0, 1fr))`}:void 0,children:P})]})})}export const MPNavigationMenu=/*@__PURE__*/t.forwardRef(function({orientation:a="horizontal",value:r,defaultValue:o,onValueChange:l,delay:c,closeDelay:p,sideOffset:d=8,size:u="md",color:f="primary",className:h,style:g,children:y,...j},b){const w=t.useMemo(()=>({size:u}),[u]);return e(v.Provider,{value:w,children:n(i.Root,{ref:b,orientation:a,value:r,defaultValue:o,onValueChange:e=>l?.(e),delay:c,closeDelay:p,"data-mp-size":u,className:["mp-navigation-menu",h??""].filter(Boolean).join(" "),style:{...s(f),...g},...j,children:[e(i.List,{className:["flex items-center","vertical"===a?"flex-col items-stretch":"flex-row",m[u]].join(" "),children:y}),e(i.Portal,{children:e(i.Positioner,{className:x,sideOffset:d,collisionPadding:12,style:s(f),children:e(i.Popup,{className:N,children:e(i.Viewport,{})})})})]})})});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{MPNavigationMenu,MPNavigationMenuItem,MPNavigationMenuLink}from"./MPNavigationMenu.js";
|
|
@@ -49,16 +49,25 @@ export interface MPNumberFieldProps extends MPStyleProps {
|
|
|
49
49
|
* report `1240`.
|
|
50
50
|
*/
|
|
51
51
|
format?: Intl.NumberFormatOptions;
|
|
52
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Which locale the number is written and parsed in. Defaults to the runtime's.
|
|
54
|
+
*
|
|
55
|
+
* A plain BCP 47 string also names the language the two steppers are announced
|
|
56
|
+
* in; anything wider falls back to the nearest `MPLocaleProvider`, then to
|
|
57
|
+
* English.
|
|
58
|
+
*/
|
|
53
59
|
locale?: Intl.LocalesArgument;
|
|
54
60
|
/**
|
|
55
61
|
* Where the steppers sit, or `none` for a field without them.
|
|
56
62
|
* @default 'end'
|
|
57
63
|
*/
|
|
58
64
|
steppers?: MPNumberFieldSteppers;
|
|
59
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* The accessible name of the increment button. Defaults to the word for it in
|
|
67
|
+
* `locale`.
|
|
68
|
+
*/
|
|
60
69
|
incrementLabel?: string;
|
|
61
|
-
/** The
|
|
70
|
+
/** The same for the decrement button. */
|
|
62
71
|
decrementLabel?: string;
|
|
63
72
|
/**
|
|
64
73
|
* Label for the field, drawn in the outline's notch and — while the field is
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as l from"react";import{NumberField as
|
|
2
|
+
import{jsx as e,jsxs as a}from"react/jsx-runtime";import*as l from"react";import{NumberField as r}from"@base-ui/react/number-field";import{Field as n}from"@base-ui/react/field";import{MPIcon as t}from"../icon/MPIcon.js";import{AddIcon as i,RemoveIcon as s}from"../../constants/icons.js";import{MPFieldLabel as o,MPFieldOutline as p,useFloatingLabel as d}from"../../internal/FieldOutline.js";import{useMPLocale as m,useMPMessages as c}from"../../internal/locale.js";import{NUMBER_FIELD as u}from"../../internal/messages/number-field.js";import{MPStateLayer as f}from"../../internal/StateLayer.js";import{MPSupportingText as h}from"../../internal/SupportingText.js";import{CONTROL_ICON as g,PROSE_TEXT as x,hasContent as b}from"../../internal/scale.js";const j={xs:{padding:"px-2",height:"h-8",stepper:"size-6"},sm:{padding:"px-3",height:"h-10",stepper:"size-8"},md:{padding:"px-4",height:"h-14",stepper:"size-10"},lg:{padding:"px-4",height:"h-16",stepper:"size-11"},xl:{padding:"px-5",height:"h-18",stepper:"size-12"}};export function MPNumberField({value:v,defaultValue:N,onValueChange:y,onValueCommitted:z,min:S,max:w,step:V,largeStep:k,smallStep:I,allowWheelScrub:q=!1,format:C,locale:L,steppers:M="end",incrementLabel:F,decrementLabel:O,label:P,floatingLabel:W=!0,description:R,errorMessage:_,placeholder:B,startIcon:D,size:G="md",fullWidth:T=!1,required:$=!1,disabled:A=!1,readOnly:E=!1,name:H,id:J}){const K=c(u,m("string"==typeof L?L:void 0)),Q=b(_),U=j[G],X=l.useId(),Y=J??X,Z="none"!==M&&!E&&!A,[ee,ae]=l.useState(N??null),le=void 0===v?ee:v,{shrunk:re,focusProps:ne}=d({floating:W&&b(P),filled:null!==le,pinned:!!D||Z&&"split"===M}),te=/*@__PURE__*/["mp-number-field__stepper group relative flex shrink-0 items-center justify-center","rounded-mp-full cursor-pointer appearance-none bg-transparent p-0 font-[inherit]","text-mp-on-surface-variant outline-none","disabled:text-mp-on-surface/38 disabled:cursor-default",U.stepper].join(" "),ie=a(r.Decrement,{"aria-label":O??K.decrease,className:te,children:[e(f,{}),e(t,{icon:s,size:g[G]})]}),se=a(r.Increment,{"aria-label":F??K.increase,className:te,children:[e(f,{}),e(t,{icon:i,size:g[G]})]});return a(n.Root,{className:["mp-number-field group flex-col align-top",T?"flex w-full":"inline-flex w-fit"].join(" "),disabled:A,invalid:Q,"data-mp-size":G,...ne,children:[e(r.Root,{id:Y,name:H,className:"contents",value:v,defaultValue:N,onValueChange:e=>{ae(e),y?.(e)},onValueCommitted:e=>z?.(e),min:S,max:w,step:V,largeStep:k,smallStep:I,allowWheelScrub:q,format:C,locale:L,disabled:A,readOnly:E,required:$,children:a("div",{className:"relative w-full",children:[a(r.Group,{className:["flex w-full items-center gap-2",U.height,Z&&"end"===M?`${U.padding} pe-1`:Z&&"split"===M?"px-1":U.padding].join(" "),children:[Z&&"split"===M?ie:null,D?e("span",{className:["flex shrink-0 items-center",A?"text-mp-on-surface/38":"text-mp-on-surface-variant"].join(" "),children:D}):null,e(r.Input,{placeholder:re?B:void 0,className:["w-full min-w-0 flex-1 tabular-nums",x[G],"font-[family-name:var(--mp-sys-typescale-body-large-font)]","appearance-none border-0 bg-transparent px-0 outline-none","caret-mp-primary text-mp-on-surface","placeholder:text-mp-on-surface-variant","disabled:text-mp-on-surface/38 disabled:placeholder:text-mp-on-surface/38",Z&&"split"===M?"text-center":""].filter(Boolean).join(" ")}),Z&&"end"===M?a("span",{className:"flex shrink-0 items-center",children:[ie,se]}):null,Z&&"split"===M?se:null]}),e(p,{label:P,required:$,notched:re}),b(P)?e(o,{size:G,label:P,required:$,htmlFor:Y,shrunk:re}):null]})}),e(h,{description:R,errorMessage:_,className:U.padding})]})}
|