@sixthshift/design-system 0.6.0 → 0.7.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/dist/components/Badge/Badge.d.ts +10 -2
- package/dist/components/Badge/Badge.d.ts.map +1 -1
- package/dist/components/Badge/Badge.js +6 -4
- package/dist/components/Badge/Badge.js.map +1 -1
- package/dist/components/Button/Button.d.ts +25 -2
- package/dist/components/Button/Button.d.ts.map +1 -1
- package/dist/components/Button/Button.js +13 -4
- package/dist/components/Button/Button.js.map +1 -1
- package/dist/components/DatePicker/DatePicker.d.ts.map +1 -1
- package/dist/components/DatePicker/DatePicker.js +2 -1
- package/dist/components/DatePicker/DatePicker.js.map +1 -1
- package/dist/components/DateTimePicker/DateTimePicker.d.ts.map +1 -1
- package/dist/components/DateTimePicker/DateTimePicker.js +2 -1
- package/dist/components/DateTimePicker/DateTimePicker.js.map +1 -1
- package/dist/components/DateTimeRangePicker/DateTimeRangePicker.d.ts.map +1 -1
- package/dist/components/DateTimeRangePicker/DateTimeRangePicker.js +2 -1
- package/dist/components/DateTimeRangePicker/DateTimeRangePicker.js.map +1 -1
- package/dist/components/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/components/HoverCard/HoverCard.js +3 -0
- package/dist/components/HoverCard/HoverCard.js.map +1 -1
- package/dist/components/HoverCard/components/HoverCardContent.d.ts +16 -2
- package/dist/components/HoverCard/components/HoverCardContent.d.ts.map +1 -1
- package/dist/components/HoverCard/components/HoverCardContent.js +3 -3
- package/dist/components/HoverCard/components/HoverCardContent.js.map +1 -1
- package/dist/components/HoverCard/components/HoverCardContext.d.ts +2 -0
- package/dist/components/HoverCard/components/HoverCardContext.d.ts.map +1 -1
- package/dist/components/HoverCard/components/HoverCardContext.js.map +1 -1
- package/dist/components/Message/Message.d.ts +4 -0
- package/dist/components/Message/Message.d.ts.map +1 -1
- package/dist/components/Message/Message.js +2 -2
- package/dist/components/Message/Message.js.map +1 -1
- package/dist/components/Modal/Modal.d.ts +12 -0
- package/dist/components/Modal/Modal.d.ts.map +1 -1
- package/dist/components/Modal/Modal.js +5 -2
- package/dist/components/Modal/Modal.js.map +1 -1
- package/dist/components/Popover/Popover.d.ts +3 -1
- package/dist/components/Popover/Popover.d.ts.map +1 -1
- package/dist/components/Popover/Popover.js +3 -0
- package/dist/components/Popover/Popover.js.map +1 -1
- package/dist/components/Popover/components/PopoverBody.d.ts +16 -2
- package/dist/components/Popover/components/PopoverBody.d.ts.map +1 -1
- package/dist/components/Popover/components/PopoverBody.js +3 -3
- package/dist/components/Popover/components/PopoverBody.js.map +1 -1
- package/dist/components/Popover/components/PopoverContext.d.ts +2 -0
- package/dist/components/Popover/components/PopoverContext.d.ts.map +1 -1
- package/dist/components/Popover/components/PopoverContext.js.map +1 -1
- package/dist/components/Select/Select.d.ts.map +1 -1
- package/dist/components/Select/Select.js +3 -2
- package/dist/components/Select/Select.js.map +1 -1
- package/dist/components/Select/SelectDropdown.d.ts +3 -1
- package/dist/components/Select/SelectDropdown.d.ts.map +1 -1
- package/dist/components/Select/SelectDropdown.js +2 -2
- package/dist/components/Select/SelectDropdown.js.map +1 -1
- package/dist/components/Sheet/Sheet.d.ts +12 -0
- package/dist/components/Sheet/Sheet.d.ts.map +1 -1
- package/dist/components/Sheet/Sheet.js +4 -2
- package/dist/components/Sheet/Sheet.js.map +1 -1
- package/dist/components/TimePicker/TimePicker.d.ts.map +1 -1
- package/dist/components/TimePicker/TimePicker.js +2 -1
- package/dist/components/TimePicker/TimePicker.js.map +1 -1
- package/dist/components/Toast/Toast.d.ts.map +1 -1
- package/dist/components/Toast/Toast.js +2 -2
- package/dist/components/Toast/Toast.js.map +1 -1
- package/dist/components/Toggle/Toggle.d.ts +4 -0
- package/dist/components/Toggle/Toggle.d.ts.map +1 -1
- package/dist/components/Toggle/Toggle.js +2 -2
- package/dist/components/Toggle/Toggle.js.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroupMultiple.d.ts +1 -0
- package/dist/components/ToggleGroup/ToggleGroupMultiple.d.ts.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroupMultiple.js +2 -2
- package/dist/components/ToggleGroup/ToggleGroupMultiple.js.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroupSingle.d.ts +1 -0
- package/dist/components/ToggleGroup/ToggleGroupSingle.d.ts.map +1 -1
- package/dist/components/ToggleGroup/ToggleGroupSingle.js +2 -2
- package/dist/components/ToggleGroup/ToggleGroupSingle.js.map +1 -1
- package/dist/components/ToggleGroup/toggleGroup.types.d.ts +6 -0
- package/dist/components/ToggleGroup/toggleGroup.types.d.ts.map +1 -1
- package/dist/components/Tooltip/Tooltip.d.ts +3 -1
- package/dist/components/Tooltip/Tooltip.d.ts.map +1 -1
- package/dist/components/Tooltip/Tooltip.js +3 -0
- package/dist/components/Tooltip/Tooltip.js.map +1 -1
- package/dist/components/Tooltip/components/TooltipBody.d.ts +16 -2
- package/dist/components/Tooltip/components/TooltipBody.d.ts.map +1 -1
- package/dist/components/Tooltip/components/TooltipBody.js +3 -3
- package/dist/components/Tooltip/components/TooltipBody.js.map +1 -1
- package/dist/components/Tooltip/components/TooltipContext.d.ts +2 -0
- package/dist/components/Tooltip/components/TooltipContext.d.ts.map +1 -1
- package/dist/components/Tooltip/components/TooltipContext.js.map +1 -1
- package/dist/internal/brandScope.d.ts +25 -0
- package/dist/internal/brandScope.d.ts.map +1 -0
- package/dist/internal/brandScope.js +37 -0
- package/dist/internal/brandScope.js.map +1 -0
- package/package.json +1 -1
- package/src/components/Badge/Badge.tsx +10 -4
- package/src/components/Button/Button.tsx +24 -4
- package/src/components/DatePicker/DatePicker.tsx +3 -0
- package/src/components/DateTimePicker/DateTimePicker.tsx +3 -0
- package/src/components/DateTimeRangePicker/DateTimeRangePicker.tsx +3 -0
- package/src/components/HoverCard/HoverCard.tsx +3 -0
- package/src/components/HoverCard/components/HoverCardContent.tsx +11 -3
- package/src/components/HoverCard/components/HoverCardContext.tsx +2 -0
- package/src/components/Message/Message.tsx +26 -22
- package/src/components/Modal/Modal.tsx +11 -0
- package/src/components/Popover/Popover.tsx +3 -0
- package/src/components/Popover/components/PopoverBody.tsx +11 -3
- package/src/components/Popover/components/PopoverContext.tsx +2 -0
- package/src/components/Select/Select.tsx +3 -1
- package/src/components/Select/SelectDropdown.tsx +4 -0
- package/src/components/Sheet/Sheet.tsx +10 -0
- package/src/components/TimePicker/TimePicker.tsx +3 -0
- package/src/components/Toast/Toast.tsx +5 -1
- package/src/components/Toggle/Toggle.tsx +17 -2
- package/src/components/ToggleGroup/ToggleGroupMultiple.tsx +2 -1
- package/src/components/ToggleGroup/ToggleGroupSingle.tsx +2 -0
- package/src/components/ToggleGroup/toggleGroup.types.ts +6 -0
- package/src/components/Tooltip/Tooltip.tsx +3 -0
- package/src/components/Tooltip/components/TooltipBody.tsx +11 -3
- package/src/components/Tooltip/components/TooltipContext.tsx +2 -0
- package/src/internal/brandScope.ts +39 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* The brand an element sits in: the `data-brand` of it or its nearest
|
|
5
|
+
* ancestor, or `undefined` outside any scope.
|
|
6
|
+
*
|
|
7
|
+
* Overlays need this because a portal moves their content out from under the
|
|
8
|
+
* element that set `data-brand`, so CSS inheritance no longer carries it. The
|
|
9
|
+
* overlay reads the brand off its trigger and re-stamps it on its own element.
|
|
10
|
+
* The value is copied verbatim, unknown names included: the theme only gives
|
|
11
|
+
* listed names meaning, so an unknown one resolves exactly as it would have in
|
|
12
|
+
* place.
|
|
13
|
+
*/
|
|
14
|
+
export function closestBrand(element) {
|
|
15
|
+
return element?.closest?.("[data-brand]")?.getAttribute("data-brand") ?? undefined;
|
|
16
|
+
}
|
|
17
|
+
const focusedBrand = () => (typeof document === "undefined" ? undefined : closestBrand(document.activeElement));
|
|
18
|
+
/**
|
|
19
|
+
* The brand of whatever held focus when the calling overlay opened.
|
|
20
|
+
*
|
|
21
|
+
* For Modal and Sheet, which have no reference element: the thing that opened
|
|
22
|
+
* them is almost always the focused control, and it still holds focus during
|
|
23
|
+
* the render that opens them, before the focus manager moves it. Captured on
|
|
24
|
+
* each closed → open transition and kept through the close, so neither focus
|
|
25
|
+
* moving into the dialog nor the exit animation changes it. When nothing useful
|
|
26
|
+
* was focused (Safari does not focus a clicked button) this is `undefined`, and
|
|
27
|
+
* the overlay inherits from wherever its portal landed.
|
|
28
|
+
*/
|
|
29
|
+
export function useOpenerBrand(open) {
|
|
30
|
+
const [captured, setCaptured] = useState(() => ({ open, brand: open ? focusedBrand() : undefined }));
|
|
31
|
+
// Adjusting state during render, React's pattern for deriving from a prop
|
|
32
|
+
// change: an effect would run after the focus manager has already moved focus.
|
|
33
|
+
if (captured.open !== open)
|
|
34
|
+
setCaptured({ open, brand: open ? focusedBrand() : captured.brand });
|
|
35
|
+
return captured.brand;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=brandScope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brandScope.js","sourceRoot":"","sources":["../../src/internal/brandScope.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEjC;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAAC,OAAmC;IAC9D,OAAO,OAAO,EAAE,OAAO,EAAE,CAAC,cAAc,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC,IAAI,SAAS,CAAC;AACrF,CAAC;AAED,MAAM,YAAY,GAAG,GAAG,EAAE,CAAC,CAAC,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAC;AAEhH;;;;;;;;;;GAUG;AACH,MAAM,UAAU,cAAc,CAAC,IAAa;IAC1C,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IACrG,0EAA0E;IAC1E,+EAA+E;IAC/E,IAAI,QAAQ,CAAC,IAAI,KAAK,IAAI;QAAE,WAAW,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IACjG,OAAO,QAAQ,CAAC,KAAK,CAAC;AACxB,CAAC"}
|
package/package.json
CHANGED
|
@@ -55,23 +55,29 @@ export type BadgeIntent = Loose<BadgeIntentName>;
|
|
|
55
55
|
export type BadgeRecipeProps = VariantProps<typeof badgeVariants> & {
|
|
56
56
|
variant?: BadgeVariant | undefined;
|
|
57
57
|
intent?: BadgeIntent | undefined;
|
|
58
|
+
/** Re-points this element's brand tokens to a brand the theme defines. See the Scoped brands docs. */
|
|
59
|
+
brand?: string | undefined;
|
|
58
60
|
className?: string | undefined;
|
|
59
61
|
};
|
|
60
62
|
|
|
61
63
|
/**
|
|
62
|
-
* The class string plus the
|
|
64
|
+
* The class string plus the attributes the recipe selects on. `data-brand` is
|
|
65
|
+
* omitted unless `brand` is set, same as `buttonRecipe`.
|
|
63
66
|
*/
|
|
64
|
-
export function badgeRecipe({ variant = "solid", intent = "brand", className }: BadgeRecipeProps) {
|
|
67
|
+
export function badgeRecipe({ variant = "solid", intent = "brand", brand, className }: BadgeRecipeProps) {
|
|
65
68
|
return {
|
|
66
69
|
className: cn(variantStructure[variant], badgeVariants({ className })),
|
|
67
70
|
"data-variant": variant,
|
|
68
71
|
"data-intent": intent,
|
|
72
|
+
"data-brand": brand,
|
|
69
73
|
};
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
export type BadgeProps = React.HTMLAttributes<HTMLSpanElement> & {
|
|
73
77
|
variant?: BadgeVariant | undefined;
|
|
74
78
|
intent?: BadgeIntent | undefined;
|
|
79
|
+
/** Name of a brand the theme defines, rendered as `data-brand`. Omitted, the badge follows the nearest `data-brand` ancestor. */
|
|
80
|
+
brand?: string | undefined;
|
|
75
81
|
};
|
|
76
82
|
|
|
77
83
|
/**
|
|
@@ -89,8 +95,8 @@ export type BadgeProps = React.HTMLAttributes<HTMLSpanElement> & {
|
|
|
89
95
|
* same seam Button uses, so a consumer can re-point a cell without a
|
|
90
96
|
* release.
|
|
91
97
|
*/
|
|
92
|
-
export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(({ className, variant = "solid", intent = "brand", ...props }, ref) => {
|
|
93
|
-
return <span ref={ref} {...badgeRecipe({ variant, intent, className })} {...props} />;
|
|
98
|
+
export const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(({ className, variant = "solid", intent = "brand", brand, ...props }, ref) => {
|
|
99
|
+
return <span ref={ref} {...badgeRecipe({ variant, intent, brand, className })} {...props} />;
|
|
94
100
|
});
|
|
95
101
|
Badge.displayName = "Badge";
|
|
96
102
|
|
|
@@ -86,18 +86,23 @@ export type ButtonIntent = Loose<ButtonIntentName>;
|
|
|
86
86
|
export type ButtonRecipeProps = VariantProps<typeof buttonVariants> & {
|
|
87
87
|
variant?: ButtonVariant | undefined;
|
|
88
88
|
intent?: ButtonIntent | undefined;
|
|
89
|
+
/** Re-points this element's brand tokens to a brand the theme defines. See the Scoped brands docs. */
|
|
90
|
+
brand?: string | undefined;
|
|
89
91
|
iconOnly?: boolean | undefined;
|
|
90
92
|
className?: string | undefined;
|
|
91
93
|
};
|
|
92
94
|
|
|
93
95
|
/**
|
|
94
|
-
* The class string plus the
|
|
96
|
+
* The class string plus the attributes the recipe selects on.
|
|
97
|
+
*
|
|
98
|
+
* `data-brand` is only emitted when `brand` is set: `undefined` makes React
|
|
99
|
+
* omit the attribute, so the element takes its brand from its surroundings.
|
|
95
100
|
*
|
|
96
101
|
* Shared with Toggle and ToggleGroupItem, which are built on Button's look. The
|
|
97
102
|
* data attributes are half the contract now, so anything reusing that look has
|
|
98
103
|
* to emit them as well or it lands on the recipe's floor instead of a cell.
|
|
99
104
|
*/
|
|
100
|
-
export function buttonRecipe({ variant = "solid", intent = "neutral", size, iconOnly = false, className }: ButtonRecipeProps) {
|
|
105
|
+
export function buttonRecipe({ variant = "solid", intent = "neutral", brand, size, iconOnly = false, className }: ButtonRecipeProps) {
|
|
101
106
|
return {
|
|
102
107
|
className: cn(
|
|
103
108
|
variantStructure[variant],
|
|
@@ -106,6 +111,7 @@ export function buttonRecipe({ variant = "solid", intent = "neutral", size, icon
|
|
|
106
111
|
),
|
|
107
112
|
"data-variant": variant,
|
|
108
113
|
"data-intent": intent,
|
|
114
|
+
"data-brand": brand,
|
|
109
115
|
};
|
|
110
116
|
}
|
|
111
117
|
|
|
@@ -113,6 +119,12 @@ export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
|
|
|
113
119
|
VariantProps<typeof buttonVariants> & {
|
|
114
120
|
variant?: ButtonVariant | undefined;
|
|
115
121
|
intent?: ButtonIntent | undefined;
|
|
122
|
+
/**
|
|
123
|
+
* Name of a brand the theme defines, rendered as `data-brand`. Re-points
|
|
124
|
+
* every brand token on this element, so `intent="brand"` takes that brand's
|
|
125
|
+
* colour. Omitted, the button follows the nearest `data-brand` ancestor.
|
|
126
|
+
*/
|
|
127
|
+
brand?: string | undefined;
|
|
116
128
|
/** Square the button at its current size, for an icon with no label. */
|
|
117
129
|
iconOnly?: boolean;
|
|
118
130
|
asChild?: boolean;
|
|
@@ -147,15 +159,23 @@ export type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> &
|
|
|
147
159
|
* `aria-label`, since there is no text to name it. It is a separate prop rather
|
|
148
160
|
* than a size, so `size="sm" iconOnly` is expressible.
|
|
149
161
|
*
|
|
162
|
+
* `brand` scopes the `brand` intent to one of the theme's named brands
|
|
163
|
+
* (`<Button intent="brand" brand="mint">`), for a theme with more than one
|
|
164
|
+
* brand colour. It renders `data-brand` and nothing else; the theme's CSS gives
|
|
165
|
+
* the name meaning, and an unknown name falls back to the surrounding brand.
|
|
166
|
+
*
|
|
150
167
|
* `asChild` renders the child element with Button's styling instead of a
|
|
151
168
|
* `<button>`, for links that should look like buttons. `loading` shows a spinner
|
|
152
169
|
* and disables the control, so it does not need `disabled` as well.
|
|
153
170
|
*/
|
|
154
171
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
|
155
|
-
(
|
|
172
|
+
(
|
|
173
|
+
{ className, variant = "solid", intent = "neutral", brand, size, iconOnly = false, asChild = false, loading = false, children, disabled, ...props },
|
|
174
|
+
ref
|
|
175
|
+
) => {
|
|
156
176
|
const Comp = asChild ? Slot : "button";
|
|
157
177
|
return (
|
|
158
|
-
<Comp {...buttonRecipe({ variant, intent, size, iconOnly, className })} ref={ref} disabled={disabled || loading} {...props}>
|
|
178
|
+
<Comp {...buttonRecipe({ variant, intent, brand, size, iconOnly, className })} ref={ref} disabled={disabled || loading} {...props}>
|
|
159
179
|
{loading ? (
|
|
160
180
|
<>
|
|
161
181
|
<svg className="animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" aria-hidden="true">
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
toISODateRange,
|
|
22
22
|
} from "../../date-time";
|
|
23
23
|
import { PickerField } from "../../internal";
|
|
24
|
+
import { closestBrand } from "../../internal/brandScope";
|
|
24
25
|
import { useEscapeLayer } from "../../internal/escapeLayers";
|
|
25
26
|
import { CalendarView } from "../Calendar/CalendarView";
|
|
26
27
|
import { isDateDisabled } from "../Calendar/calendar.hooks";
|
|
@@ -539,6 +540,8 @@ export const DatePicker = React.forwardRef<HTMLDivElement, DatePickerProps>((pro
|
|
|
539
540
|
role="dialog"
|
|
540
541
|
aria-label="Choose date"
|
|
541
542
|
style={floatingStyles}
|
|
543
|
+
// Portalled out of the trigger's brand scope, so carry it across.
|
|
544
|
+
data-brand={closestBrand(context.elements.domReference)}
|
|
542
545
|
className="z-popover rounded-xl border border-border-normal bg-bg-normal p-4 shadow-lg"
|
|
543
546
|
{...getFloatingProps()}
|
|
544
547
|
>
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
toISOInstantOrUndefined,
|
|
17
17
|
} from "../../date-time";
|
|
18
18
|
import { PickerField } from "../../internal";
|
|
19
|
+
import { closestBrand } from "../../internal/brandScope";
|
|
19
20
|
import { useEscapeLayer } from "../../internal/escapeLayers";
|
|
20
21
|
import { Button } from "../Button";
|
|
21
22
|
import { CalendarView } from "../Calendar/CalendarView";
|
|
@@ -410,6 +411,8 @@ export const DateTimePicker = React.forwardRef<HTMLDivElement, DateTimePickerPro
|
|
|
410
411
|
role="dialog"
|
|
411
412
|
aria-label="Choose date and time"
|
|
412
413
|
style={floatingStyles}
|
|
414
|
+
// Portalled out of the trigger's brand scope, so carry it across.
|
|
415
|
+
data-brand={closestBrand(context.elements.domReference)}
|
|
413
416
|
className="z-popover rounded-xl border border-border-normal bg-bg-normal p-4 shadow-lg"
|
|
414
417
|
{...getFloatingProps()}
|
|
415
418
|
>
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
toISOInstantRange,
|
|
20
20
|
} from "../../date-time";
|
|
21
21
|
import { PickerField } from "../../internal";
|
|
22
|
+
import { closestBrand } from "../../internal/brandScope";
|
|
22
23
|
import { useEscapeLayer } from "../../internal/escapeLayers";
|
|
23
24
|
import { Button } from "../Button";
|
|
24
25
|
import { CalendarView } from "../Calendar/CalendarView";
|
|
@@ -630,6 +631,8 @@ export const DateTimeRangePicker = React.forwardRef<HTMLDivElement, DateTimeRang
|
|
|
630
631
|
role="dialog"
|
|
631
632
|
aria-label="Choose date and time range"
|
|
632
633
|
style={floatingStyles}
|
|
634
|
+
// Portalled out of the trigger's brand scope, so carry it across.
|
|
635
|
+
data-brand={closestBrand(context.elements.domReference)}
|
|
633
636
|
className="z-popover rounded-xl border border-border-normal bg-bg-normal p-4 shadow-lg"
|
|
634
637
|
{...getFloatingProps()}
|
|
635
638
|
>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { autoUpdate, flip, offset, type Placement, safePolygon, shift, useDismiss, useFloating, useFocus, useHover, useInteractions } from "@floating-ui/react";
|
|
4
4
|
import { useControllableState } from "@sixthshift/design-system/hooks";
|
|
5
5
|
import type * as React from "react";
|
|
6
|
+
import { closestBrand } from "../../internal/brandScope";
|
|
6
7
|
import { HoverCardContent } from "./components/HoverCardContent";
|
|
7
8
|
import { HoverCardContext } from "./components/HoverCardContext";
|
|
8
9
|
import { HoverCardTrigger } from "./components/HoverCardTrigger";
|
|
@@ -67,6 +68,8 @@ const HoverCardRoot = ({
|
|
|
67
68
|
setFloating: refs.setFloating,
|
|
68
69
|
},
|
|
69
70
|
floatingStyles,
|
|
71
|
+
// Read at render: `domReference` is state, so this updates once the trigger mounts.
|
|
72
|
+
inheritedBrand: closestBrand(context.elements.domReference),
|
|
70
73
|
getReferenceProps,
|
|
71
74
|
getFloatingProps,
|
|
72
75
|
};
|
|
@@ -5,10 +5,17 @@ import { cn } from "@sixthshift/design-system/utils";
|
|
|
5
5
|
import * as React from "react";
|
|
6
6
|
import { useHoverCardContext } from "./HoverCardContext";
|
|
7
7
|
|
|
8
|
-
export type HoverCardContentProps = React.HTMLAttributes<HTMLDivElement
|
|
8
|
+
export type HoverCardContentProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
9
|
+
/**
|
|
10
|
+
* Name of a brand the theme defines, rendered as `data-brand`. Omitted, the
|
|
11
|
+
* body copies the brand scope of its trigger, which the portal would
|
|
12
|
+
* otherwise leave behind.
|
|
13
|
+
*/
|
|
14
|
+
brand?: string | undefined;
|
|
15
|
+
};
|
|
9
16
|
|
|
10
|
-
export const HoverCardContent = React.forwardRef<HTMLDivElement, HoverCardContentProps>(({ className, children, ...props }, forwardedRef) => {
|
|
11
|
-
const { open, refs, floatingStyles, getFloatingProps } = useHoverCardContext();
|
|
17
|
+
export const HoverCardContent = React.forwardRef<HTMLDivElement, HoverCardContentProps>(({ className, brand, children, ...props }, forwardedRef) => {
|
|
18
|
+
const { open, refs, floatingStyles, inheritedBrand, getFloatingProps } = useHoverCardContext();
|
|
12
19
|
|
|
13
20
|
if (!open) return null;
|
|
14
21
|
|
|
@@ -21,6 +28,7 @@ export const HoverCardContent = React.forwardRef<HTMLDivElement, HoverCardConten
|
|
|
21
28
|
else if (forwardedRef) forwardedRef.current = node;
|
|
22
29
|
}}
|
|
23
30
|
style={floatingStyles}
|
|
31
|
+
data-brand={brand ?? inheritedBrand}
|
|
24
32
|
className={cn("z-popover w-80 rounded-lg border border-border-normal bg-bg-normal p-4 shadow-lg", className)}
|
|
25
33
|
{...getFloatingProps(props)}
|
|
26
34
|
>
|
|
@@ -9,6 +9,8 @@ export type HoverCardContextValue = {
|
|
|
9
9
|
setFloating: (node: HTMLElement | null) => void;
|
|
10
10
|
};
|
|
11
11
|
floatingStyles: React.CSSProperties;
|
|
12
|
+
/** The trigger's brand scope, re-stamped on the portalled body. */
|
|
13
|
+
inheritedBrand: string | undefined;
|
|
12
14
|
getReferenceProps: (props?: React.HTMLProps<HTMLElement>) => Record<string, unknown>;
|
|
13
15
|
getFloatingProps: (props?: React.HTMLProps<HTMLElement>) => Record<string, unknown>;
|
|
14
16
|
};
|
|
@@ -48,6 +48,8 @@ export type MessageIntent = Loose<MessageIntentName>;
|
|
|
48
48
|
export type MessageProps = React.HTMLAttributes<HTMLDivElement> &
|
|
49
49
|
VariantProps<typeof messageVariants> & {
|
|
50
50
|
intent?: MessageIntent | undefined;
|
|
51
|
+
/** Name of a brand the theme defines, rendered as `data-brand`. Omitted, the message follows the nearest `data-brand` ancestor. */
|
|
52
|
+
brand?: string | undefined;
|
|
51
53
|
/** Simple API: Optional title displayed above children */
|
|
52
54
|
title?: React.ReactNode;
|
|
53
55
|
/** Simple API: Optional icon displayed on the left */
|
|
@@ -99,27 +101,29 @@ function intentRole(intent: MessageIntent): "status" | "alert" {
|
|
|
99
101
|
* simple API can't express. Message detects which one is in use by
|
|
100
102
|
* inspecting its children, so the two aren't meant to be mixed.
|
|
101
103
|
*/
|
|
102
|
-
export const Message = React.forwardRef<HTMLDivElement, MessageProps>(
|
|
103
|
-
|
|
104
|
-
(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
104
|
+
export const Message = React.forwardRef<HTMLDivElement, MessageProps>(
|
|
105
|
+
({ className, intent = "neutral", brand, size, title, icon, children, ...props }, ref) => {
|
|
106
|
+
const hasCompoundChildren = React.Children.toArray(children).some(
|
|
107
|
+
(child) =>
|
|
108
|
+
React.isValidElement(child) &&
|
|
109
|
+
(child.type === MessageIcon || child.type === MessageBody || child.type === MessageTitle || child.type === MessageDescription)
|
|
110
|
+
);
|
|
108
111
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
112
|
+
return (
|
|
113
|
+
<div ref={ref} role={intentRole(intent)} data-intent={intent} data-brand={brand} className={cn(messageVariants({ size, className }))} {...props}>
|
|
114
|
+
{hasCompoundChildren ? (
|
|
115
|
+
children
|
|
116
|
+
) : (
|
|
117
|
+
<>
|
|
118
|
+
{icon && <MessageIcon>{icon}</MessageIcon>}
|
|
119
|
+
<MessageBody>
|
|
120
|
+
{title && <MessageTitle>{title}</MessageTitle>}
|
|
121
|
+
<MessageDescription>{children}</MessageDescription>
|
|
122
|
+
</MessageBody>
|
|
123
|
+
</>
|
|
124
|
+
)}
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
);
|
|
125
129
|
Message.displayName = "Message";
|
|
@@ -4,6 +4,7 @@ import { FloatingFocusManager, FloatingOverlay, FloatingPortal, useDismiss, useF
|
|
|
4
4
|
import { useMergedFloatingRef, usePresence } from "@sixthshift/design-system/hooks";
|
|
5
5
|
import { cn } from "@sixthshift/design-system/utils";
|
|
6
6
|
import { forwardRef, type HTMLAttributes, type ReactNode, useCallback, useContext, useEffect, useId, useMemo, useState } from "react";
|
|
7
|
+
import { useOpenerBrand } from "../../internal/brandScope";
|
|
7
8
|
import { hasOpenEscapeLayer, useEscapeDialog } from "../../internal/escapeLayers";
|
|
8
9
|
import { ModalBody, ModalContext, ModalFooter, ModalHeader } from "./components";
|
|
9
10
|
|
|
@@ -35,6 +36,12 @@ export type ModalProps = Pick<HTMLAttributes<HTMLDivElement>, "className" | "sty
|
|
|
35
36
|
closable?: boolean;
|
|
36
37
|
/** Vertical alignment on desktop. "center" (default) or "top" (anchored near top, content grows down) */
|
|
37
38
|
align?: "center" | "top";
|
|
39
|
+
/**
|
|
40
|
+
* Name of a brand the theme defines, rendered as `data-brand` on the dialog.
|
|
41
|
+
* Omitted, the modal takes the brand scope of the control that opened it
|
|
42
|
+
* (whatever held focus), since the portal leaves the trigger's scope behind.
|
|
43
|
+
*/
|
|
44
|
+
brand?: string | undefined;
|
|
38
45
|
};
|
|
39
46
|
|
|
40
47
|
// =============================================================================
|
|
@@ -63,6 +70,7 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
|
|
|
63
70
|
dismissable = true,
|
|
64
71
|
closable,
|
|
65
72
|
align = "center",
|
|
73
|
+
brand,
|
|
66
74
|
"aria-label": ariaLabel,
|
|
67
75
|
"aria-labelledby": ariaLabelledBy,
|
|
68
76
|
"aria-describedby": ariaDescribedBy,
|
|
@@ -81,6 +89,8 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
|
|
|
81
89
|
}, [onOpenChange, contextValue]);
|
|
82
90
|
|
|
83
91
|
const { ref: presenceRef, state, isMounted, show, hide } = usePresence();
|
|
92
|
+
// Mount-driven, so the modal is open for as long as it exists.
|
|
93
|
+
const openerBrand = useOpenerBrand(true);
|
|
84
94
|
|
|
85
95
|
useEffect(() => {
|
|
86
96
|
show();
|
|
@@ -163,6 +173,7 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(
|
|
|
163
173
|
aria-describedby={ariaDescribedBy}
|
|
164
174
|
tabIndex={-1}
|
|
165
175
|
data-state={state}
|
|
176
|
+
data-brand={brand ?? openerBrand}
|
|
166
177
|
className={cn(
|
|
167
178
|
"modal fixed flex flex-col overflow-hidden outline-hidden",
|
|
168
179
|
"border-(color:--modal-border) rounded-xl border bg-(--modal-bg)",
|
|
@@ -4,6 +4,7 @@ import { autoUpdate, flip, offset, type Placement, shift, useClick, useDismiss,
|
|
|
4
4
|
import { useControllableState } from "@sixthshift/design-system/hooks";
|
|
5
5
|
import type * as React from "react";
|
|
6
6
|
import { useId } from "react";
|
|
7
|
+
import { closestBrand } from "../../internal/brandScope";
|
|
7
8
|
import { useEscapeLayer } from "../../internal/escapeLayers";
|
|
8
9
|
import { PopoverBody } from "./components/PopoverBody";
|
|
9
10
|
import { PopoverClose } from "./components/PopoverClose";
|
|
@@ -66,6 +67,8 @@ const PopoverRoot = ({ open: controlledOpen, onOpenChange, defaultOpen = false,
|
|
|
66
67
|
setFloating: refs.setFloating,
|
|
67
68
|
},
|
|
68
69
|
floatingStyles,
|
|
70
|
+
// Read at render: `domReference` is state, so this updates once the trigger mounts.
|
|
71
|
+
inheritedBrand: closestBrand(context.elements.domReference),
|
|
69
72
|
getReferenceProps,
|
|
70
73
|
getFloatingProps,
|
|
71
74
|
contentId,
|
|
@@ -5,10 +5,17 @@ import { cn } from "@sixthshift/design-system/utils";
|
|
|
5
5
|
import * as React from "react";
|
|
6
6
|
import { usePopoverContext } from "./PopoverContext";
|
|
7
7
|
|
|
8
|
-
export type PopoverBodyProps = React.HTMLAttributes<HTMLDivElement
|
|
8
|
+
export type PopoverBodyProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
9
|
+
/**
|
|
10
|
+
* Name of a brand the theme defines, rendered as `data-brand`. Omitted, the
|
|
11
|
+
* body copies the brand scope of its trigger, which the portal would
|
|
12
|
+
* otherwise leave behind.
|
|
13
|
+
*/
|
|
14
|
+
brand?: string | undefined;
|
|
15
|
+
};
|
|
9
16
|
|
|
10
|
-
export const PopoverBody = React.forwardRef<HTMLDivElement, PopoverBodyProps>(({ className, children, ...props }, forwardedRef) => {
|
|
11
|
-
const { open, refs, floatingStyles, getFloatingProps, contentId, triggerId } = usePopoverContext();
|
|
17
|
+
export const PopoverBody = React.forwardRef<HTMLDivElement, PopoverBodyProps>(({ className, brand, children, ...props }, forwardedRef) => {
|
|
18
|
+
const { open, refs, floatingStyles, inheritedBrand, getFloatingProps, contentId, triggerId } = usePopoverContext();
|
|
12
19
|
|
|
13
20
|
// `aria-labelledby` outranks `aria-label` in the name computation, so the
|
|
14
21
|
// trigger only names the dialog when the caller hasn't named it themselves.
|
|
@@ -28,6 +35,7 @@ export const PopoverBody = React.forwardRef<HTMLDivElement, PopoverBodyProps>(({
|
|
|
28
35
|
role="dialog"
|
|
29
36
|
aria-labelledby={hasOwnLabel ? undefined : triggerId}
|
|
30
37
|
style={floatingStyles}
|
|
38
|
+
data-brand={brand ?? inheritedBrand}
|
|
31
39
|
className={cn("z-popover rounded-lg border border-border-normal bg-bg-normal p-4 shadow-lg", className)}
|
|
32
40
|
{...getFloatingProps(props)}
|
|
33
41
|
>
|
|
@@ -10,6 +10,8 @@ export type PopoverContextValue = {
|
|
|
10
10
|
setFloating: (node: HTMLElement | null) => void;
|
|
11
11
|
};
|
|
12
12
|
floatingStyles: React.CSSProperties;
|
|
13
|
+
/** The trigger's brand scope, re-stamped on the portalled body. */
|
|
14
|
+
inheritedBrand: string | undefined;
|
|
13
15
|
getReferenceProps: (props?: React.HTMLProps<HTMLElement>) => Record<string, unknown>;
|
|
14
16
|
getFloatingProps: (props?: React.HTMLProps<HTMLElement>) => Record<string, unknown>;
|
|
15
17
|
contentId: string;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { autoUpdate, flip, offset, shift, size, useFloating } from "@floating-ui/react";
|
|
4
4
|
import { useControllableState } from "@sixthshift/design-system/hooks";
|
|
5
5
|
import { forwardRef, type HTMLAttributes, type ReactElement, type Ref, useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
|
|
6
|
+
import { closestBrand } from "../../internal/brandScope";
|
|
6
7
|
import { useEscapeLayer } from "../../internal/escapeLayers";
|
|
7
8
|
import { SelectDropdown, selectOptionId } from "./SelectDropdown";
|
|
8
9
|
import { SelectTriggerButton, SelectTriggerSearch } from "./SelectTrigger";
|
|
@@ -117,7 +118,7 @@ const SelectRoot = forwardRef(function SelectRoot<T extends string = string>(pro
|
|
|
117
118
|
const showClearButton = clearable && hasValue && !disabled;
|
|
118
119
|
|
|
119
120
|
// Floating UI
|
|
120
|
-
const { refs, floatingStyles } = useFloating({
|
|
121
|
+
const { refs, elements, floatingStyles } = useFloating({
|
|
121
122
|
open,
|
|
122
123
|
onOpenChange: setOpen,
|
|
123
124
|
placement: "bottom-start",
|
|
@@ -342,6 +343,7 @@ const SelectRoot = forwardRef(function SelectRoot<T extends string = string>(pro
|
|
|
342
343
|
label={listboxLabel}
|
|
343
344
|
activeOptionId={activeOptionId}
|
|
344
345
|
floatingStyles={floatingStyles}
|
|
346
|
+
brand={closestBrand(elements.domReference)}
|
|
345
347
|
displayOptions={filteredOptions}
|
|
346
348
|
highlightedIndex={highlightedIndex}
|
|
347
349
|
selectedValues={selectedValues}
|
|
@@ -23,6 +23,8 @@ interface SelectDropdownProps<T extends string> {
|
|
|
23
23
|
/** The highlighted option's id, or undefined when nothing is highlighted. */
|
|
24
24
|
activeOptionId: string | undefined;
|
|
25
25
|
floatingStyles: React.CSSProperties;
|
|
26
|
+
/** The trigger's brand scope, re-stamped here because the portal leaves it behind. */
|
|
27
|
+
brand: string | undefined;
|
|
26
28
|
displayOptions: readonly { value: T; label: string }[];
|
|
27
29
|
highlightedIndex: number;
|
|
28
30
|
selectedValues: Set<T>;
|
|
@@ -41,6 +43,7 @@ export const SelectDropdown = <T extends string>({
|
|
|
41
43
|
label,
|
|
42
44
|
activeOptionId,
|
|
43
45
|
floatingStyles,
|
|
46
|
+
brand,
|
|
44
47
|
displayOptions,
|
|
45
48
|
highlightedIndex,
|
|
46
49
|
selectedValues,
|
|
@@ -68,6 +71,7 @@ export const SelectDropdown = <T extends string>({
|
|
|
68
71
|
tabIndex={-1}
|
|
69
72
|
onKeyDown={onKeyDown}
|
|
70
73
|
style={floatingStyles}
|
|
74
|
+
data-brand={brand}
|
|
71
75
|
className="select-dropdown border-(color:--select-dropdown-border) z-popover max-h-60 overflow-y-auto rounded-md border bg-(--select-dropdown-bg) shadow-lg"
|
|
72
76
|
>
|
|
73
77
|
{displayOptions.length === 0 ? (
|
|
@@ -4,6 +4,7 @@ import { FloatingFocusManager, FloatingPortal, useDismiss, useFloating, useInter
|
|
|
4
4
|
import { useMergedFloatingRef, usePresence } from "@sixthshift/design-system/hooks";
|
|
5
5
|
import { cn } from "@sixthshift/design-system/utils";
|
|
6
6
|
import { forwardRef, type HTMLAttributes, type ReactNode, useCallback, useEffect, useId, useMemo, useState } from "react";
|
|
7
|
+
import { useOpenerBrand } from "../../internal/brandScope";
|
|
7
8
|
import { hasOpenEscapeDialog, hasOpenEscapeLayer } from "../../internal/escapeLayers";
|
|
8
9
|
import { SheetBody, SheetContext, SheetFooter, SheetHeader } from "./components";
|
|
9
10
|
|
|
@@ -26,6 +27,12 @@ export type SheetProps = Pick<HTMLAttributes<HTMLDivElement>, "className" | "sty
|
|
|
26
27
|
dismissOnOutsidePress?: boolean;
|
|
27
28
|
/** When true, shows a close (X) button in the header */
|
|
28
29
|
closable?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Name of a brand the theme defines, rendered as `data-brand` on the sheet.
|
|
32
|
+
* Omitted, the sheet takes the brand scope of the control that opened it
|
|
33
|
+
* (whatever held focus), since the portal leaves the trigger's scope behind.
|
|
34
|
+
*/
|
|
35
|
+
brand?: string | undefined;
|
|
29
36
|
/** Children — use Sheet.Header, Sheet.Body, Sheet.Footer */
|
|
30
37
|
children: ReactNode;
|
|
31
38
|
};
|
|
@@ -54,6 +61,7 @@ const SheetRoot = forwardRef<HTMLDivElement, SheetProps>(
|
|
|
54
61
|
dismissable = true,
|
|
55
62
|
dismissOnOutsidePress = false,
|
|
56
63
|
closable,
|
|
64
|
+
brand,
|
|
57
65
|
className,
|
|
58
66
|
style,
|
|
59
67
|
children,
|
|
@@ -64,6 +72,7 @@ const SheetRoot = forwardRef<HTMLDivElement, SheetProps>(
|
|
|
64
72
|
ref
|
|
65
73
|
) => {
|
|
66
74
|
const { ref: presenceRef, state, isMounted, show, hide } = usePresence();
|
|
75
|
+
const openerBrand = useOpenerBrand(open);
|
|
67
76
|
|
|
68
77
|
// Drive presence from the controlled `open` prop.
|
|
69
78
|
// When `open` flips to false, we play the exit animation, then unmount.
|
|
@@ -141,6 +150,7 @@ const SheetRoot = forwardRef<HTMLDivElement, SheetProps>(
|
|
|
141
150
|
tabIndex={-1}
|
|
142
151
|
data-state={state}
|
|
143
152
|
data-side={side}
|
|
153
|
+
data-brand={brand ?? openerBrand}
|
|
144
154
|
className={cn(
|
|
145
155
|
"sheet fixed top-0 bottom-0 z-sheet flex flex-col overflow-hidden bg-(--sheet-bg) outline-hidden",
|
|
146
156
|
"border-(color:--sheet-border) shadow-lg",
|
|
@@ -8,6 +8,7 @@ import * as React from "react";
|
|
|
8
8
|
import { useCallback, useId, useMemo, useState } from "react";
|
|
9
9
|
import { fromISOTime, fromISOTimeOrUndefined, type Temporal, toISOTimeOrUndefined } from "../../date-time";
|
|
10
10
|
import { PickerField } from "../../internal";
|
|
11
|
+
import { closestBrand } from "../../internal/brandScope";
|
|
11
12
|
import { useEscapeLayer } from "../../internal/escapeLayers";
|
|
12
13
|
import { Button } from "../Button";
|
|
13
14
|
import { Separator } from "../Separator";
|
|
@@ -354,6 +355,8 @@ export const TimePicker = React.forwardRef<HTMLDivElement, TimePickerProps>((pro
|
|
|
354
355
|
role="dialog"
|
|
355
356
|
aria-label="Choose time"
|
|
356
357
|
style={floatingStyles}
|
|
358
|
+
// Portalled out of the trigger's brand scope, so carry it across.
|
|
359
|
+
data-brand={closestBrand(context.elements.domReference)}
|
|
357
360
|
className="z-popover rounded-xl border border-border-normal bg-bg-normal p-4 shadow-lg"
|
|
358
361
|
{...getFloatingProps()}
|
|
359
362
|
>
|
|
@@ -68,7 +68,7 @@ export type ToastProps = Omit<MessageProps, "size"> & {
|
|
|
68
68
|
* a dialog.
|
|
69
69
|
*/
|
|
70
70
|
export const Toast = React.forwardRef<HTMLDivElement, ToastProps>(
|
|
71
|
-
({ className, intent = "neutral", title, icon, action, onAction, onClose, root, open = true, standalone = true, children, ...props }, ref) => {
|
|
71
|
+
({ className, intent = "neutral", brand, title, icon, action, onAction, onClose, root, open = true, standalone = true, children, ...props }, ref) => {
|
|
72
72
|
const { ref: presenceRef, state, isMounted, show, hide } = usePresence();
|
|
73
73
|
|
|
74
74
|
// `open` drives presence: enter on mount, exit when it flips false. The
|
|
@@ -118,6 +118,10 @@ export const Toast = React.forwardRef<HTMLDivElement, ToastProps>(
|
|
|
118
118
|
isExiting && "animate-fade-out",
|
|
119
119
|
className
|
|
120
120
|
)}
|
|
121
|
+
// On the wrapper rather than the Message, so the dismiss and action
|
|
122
|
+
// buttons are in the same scope. A toast has no trigger to inherit
|
|
123
|
+
// from: it is opened from code, often long after the click.
|
|
124
|
+
data-brand={brand}
|
|
121
125
|
{...props}
|
|
122
126
|
>
|
|
123
127
|
<Message intent={intent}>
|
|
@@ -15,6 +15,8 @@ export type ToggleProps = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "o
|
|
|
15
15
|
VariantProps<typeof buttonVariants> & {
|
|
16
16
|
variant?: ButtonVariant | undefined;
|
|
17
17
|
intent?: ButtonIntent | undefined;
|
|
18
|
+
/** Name of a brand the theme defines, rendered as `data-brand`. Omitted, the toggle follows the nearest `data-brand` ancestor. */
|
|
19
|
+
brand?: string | undefined;
|
|
18
20
|
/** Square the toggle at its current size, for an icon with no label. */
|
|
19
21
|
iconOnly?: boolean;
|
|
20
22
|
/** Controlled pressed state */
|
|
@@ -43,7 +45,20 @@ export type ToggleProps = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "o
|
|
|
43
45
|
*/
|
|
44
46
|
const Toggle = React.forwardRef<HTMLButtonElement, ToggleProps>(
|
|
45
47
|
(
|
|
46
|
-
{
|
|
48
|
+
{
|
|
49
|
+
className,
|
|
50
|
+
variant,
|
|
51
|
+
intent,
|
|
52
|
+
brand,
|
|
53
|
+
size,
|
|
54
|
+
iconOnly = false,
|
|
55
|
+
pressed: controlledPressed,
|
|
56
|
+
defaultPressed = false,
|
|
57
|
+
onPressedChange,
|
|
58
|
+
disabled,
|
|
59
|
+
children,
|
|
60
|
+
...props
|
|
61
|
+
},
|
|
47
62
|
ref
|
|
48
63
|
) => {
|
|
49
64
|
const [pressed, setPressed] = useControllableState({
|
|
@@ -52,7 +67,7 @@ const Toggle = React.forwardRef<HTMLButtonElement, ToggleProps>(
|
|
|
52
67
|
onChange: onPressedChange,
|
|
53
68
|
});
|
|
54
69
|
|
|
55
|
-
const recipe = buttonRecipe({ variant, intent, size, iconOnly });
|
|
70
|
+
const recipe = buttonRecipe({ variant, intent, brand, size, iconOnly });
|
|
56
71
|
|
|
57
72
|
return (
|
|
58
73
|
<button
|
|
@@ -17,6 +17,7 @@ const ToggleGroupMultiple = React.forwardRef<HTMLDivElement, ToggleGroupMultiple
|
|
|
17
17
|
orientation = "horizontal",
|
|
18
18
|
variant = "solid",
|
|
19
19
|
intent = "neutral",
|
|
20
|
+
brand,
|
|
20
21
|
size = "md",
|
|
21
22
|
iconOnly = false,
|
|
22
23
|
disabled,
|
|
@@ -39,7 +40,7 @@ const ToggleGroupMultiple = React.forwardRef<HTMLDivElement, ToggleGroupMultiple
|
|
|
39
40
|
};
|
|
40
41
|
|
|
41
42
|
return (
|
|
42
|
-
<ToggleGroupContainer ref={ref} role="group" appearance={appearance} orientation={orientation} className={className} {...props}>
|
|
43
|
+
<ToggleGroupContainer ref={ref} role="group" data-brand={brand} appearance={appearance} orientation={orientation} className={className} {...props}>
|
|
43
44
|
{options.map((option, index) => (
|
|
44
45
|
<ToggleGroupItem
|
|
45
46
|
key={option.value}
|