@flamingo-stack/openframe-frontend-core 0.0.503 → 0.0.504
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/{chunk-OAWFYZZT.js → chunk-32J4AXWO.js} +3 -3
- package/dist/{chunk-4UNQMYP7.cjs → chunk-5JEL52KB.cjs} +3 -3
- package/dist/{chunk-4UNQMYP7.cjs.map → chunk-5JEL52KB.cjs.map} +1 -1
- package/dist/{chunk-HR6F6IP4.js → chunk-AFHWEJXM.js} +6 -6
- package/dist/{chunk-3T4SMJM5.cjs → chunk-AI7ASU6V.cjs} +93 -93
- package/dist/{chunk-3T4SMJM5.cjs.map → chunk-AI7ASU6V.cjs.map} +1 -1
- package/dist/{chunk-FFXNRJ5S.cjs → chunk-C6Q3BJDJ.cjs} +2 -2
- package/dist/chunk-C6Q3BJDJ.cjs.map +1 -0
- package/dist/{chunk-72DF3QTA.cjs → chunk-CQ6O6ER2.cjs} +12 -12
- package/dist/{chunk-72DF3QTA.cjs.map → chunk-CQ6O6ER2.cjs.map} +1 -1
- package/dist/{chunk-VYEDTVIZ.js → chunk-CSRGIQ2G.js} +3 -3
- package/dist/{chunk-7L2RVINX.cjs → chunk-D3VTZBWC.cjs} +32 -32
- package/dist/{chunk-7L2RVINX.cjs.map → chunk-D3VTZBWC.cjs.map} +1 -1
- package/dist/{chunk-4SR2QHMB.js → chunk-EFBKOCEA.js} +3 -3
- package/dist/{chunk-JGXAVNZQ.js → chunk-FW2U6U4P.js} +45 -34
- package/dist/{chunk-JGXAVNZQ.js.map → chunk-FW2U6U4P.js.map} +1 -1
- package/dist/{chunk-BZF63JXP.js → chunk-GCVAZR52.js} +5 -5
- package/dist/{chunk-FTOVZ7O2.js → chunk-HNDHCJV4.js} +6 -6
- package/dist/{chunk-UCV4LFUG.cjs → chunk-IZTXVVGY.cjs} +40 -40
- package/dist/{chunk-UCV4LFUG.cjs.map → chunk-IZTXVVGY.cjs.map} +1 -1
- package/dist/{chunk-XV5SXALY.js → chunk-JCF55O7G.js} +2 -2
- package/dist/{chunk-CLQEBWV7.js → chunk-JQDGP4CE.js} +4 -4
- package/dist/{chunk-SGWPW2AO.cjs → chunk-KHE3OBHD.cjs} +18 -18
- package/dist/{chunk-SGWPW2AO.cjs.map → chunk-KHE3OBHD.cjs.map} +1 -1
- package/dist/{chunk-J4T5ZBWG.cjs → chunk-KZSQMZUL.cjs} +19 -19
- package/dist/{chunk-J4T5ZBWG.cjs.map → chunk-KZSQMZUL.cjs.map} +1 -1
- package/dist/{chunk-VO2ZONMR.js → chunk-MRPFFHBK.js} +3 -3
- package/dist/{chunk-JIZJDAXI.cjs → chunk-NNQMA3N6.cjs} +12 -12
- package/dist/{chunk-JIZJDAXI.cjs.map → chunk-NNQMA3N6.cjs.map} +1 -1
- package/dist/{chunk-AWDHRIL3.cjs → chunk-OK34RZWT.cjs} +73 -73
- package/dist/{chunk-AWDHRIL3.cjs.map → chunk-OK34RZWT.cjs.map} +1 -1
- package/dist/{chunk-2OXJ6NHI.cjs → chunk-Q6RMV5WK.cjs} +5 -5
- package/dist/{chunk-2OXJ6NHI.cjs.map → chunk-Q6RMV5WK.cjs.map} +1 -1
- package/dist/{chunk-G43CEISB.js → chunk-QFQBYNQJ.js} +4 -4
- package/dist/{chunk-WHJGRRK2.js → chunk-QW7SW5HY.js} +52 -43
- package/dist/chunk-QW7SW5HY.js.map +1 -0
- package/dist/{chunk-L5CI57RM.cjs → chunk-T3BF6JTQ.cjs} +52 -43
- package/dist/chunk-T3BF6JTQ.cjs.map +1 -0
- package/dist/{chunk-RR3Z52X5.cjs → chunk-US2F5GJO.cjs} +40 -40
- package/dist/{chunk-RR3Z52X5.cjs.map → chunk-US2F5GJO.cjs.map} +1 -1
- package/dist/{chunk-5RIHLXEP.cjs → chunk-WXARO2KA.cjs} +33 -33
- package/dist/{chunk-5RIHLXEP.cjs.map → chunk-WXARO2KA.cjs.map} +1 -1
- package/dist/{chunk-DMXB3VE2.cjs → chunk-X5USQ6UC.cjs} +492 -481
- package/dist/chunk-X5USQ6UC.cjs.map +1 -0
- package/dist/{chunk-SFJRMBJC.js → chunk-XRGAQM3F.js} +2 -2
- package/dist/{chunk-VCPTU4IW.js → chunk-XT4P4JGJ.js} +6 -6
- package/dist/{chunk-F7AD5CUU.js → chunk-YKF25XRY.js} +2 -2
- package/dist/chunk-YKF25XRY.js.map +1 -0
- package/dist/components/case-studies/index.cjs +10 -10
- package/dist/components/case-studies/index.js +4 -4
- package/dist/components/chat/index.cjs +4 -4
- package/dist/components/chat/index.js +3 -3
- package/dist/components/contact/index.cjs +5 -5
- package/dist/components/contact/index.js +4 -4
- package/dist/components/docs/index.cjs +7 -7
- package/dist/components/docs/index.js +6 -6
- package/dist/components/embeds/index.cjs +5 -5
- package/dist/components/embeds/index.js +4 -4
- package/dist/components/faq/index.cjs +6 -6
- package/dist/components/faq/index.js +5 -5
- package/dist/components/features/index.cjs +4 -4
- package/dist/components/features/index.js +3 -3
- package/dist/components/help-center-pages/index.cjs +23 -23
- package/dist/components/help-center-pages/index.js +14 -14
- package/dist/components/index.cjs +161 -161
- package/dist/components/index.js +12 -12
- package/dist/components/navigation/header-button.d.ts.map +1 -1
- package/dist/components/navigation/header.d.ts.map +1 -1
- package/dist/components/navigation/index.cjs +4 -4
- package/dist/components/navigation/index.js +3 -3
- package/dist/components/navigation/mingo-ai-button.d.ts.map +1 -1
- package/dist/components/navigation/top-navigation.d.ts +5 -1
- package/dist/components/navigation/top-navigation.d.ts.map +1 -1
- package/dist/components/onboarding-guides/index.cjs +8 -8
- package/dist/components/onboarding-guides/index.js +7 -7
- package/dist/components/related-content/index.cjs +6 -6
- package/dist/components/related-content/index.js +5 -5
- package/dist/components/tickets/index.cjs +7 -7
- package/dist/components/tickets/index.js +6 -6
- package/dist/components/ui/button/button.d.ts +6 -5
- package/dist/components/ui/button/button.d.ts.map +1 -1
- package/dist/components/ui/file-manager/index.cjs +50 -50
- package/dist/components/ui/file-manager/index.js +1 -1
- package/dist/components/ui/index.cjs +4 -4
- package/dist/components/ui/index.js +3 -3
- package/dist/hooks/index.cjs +2 -2
- package/dist/hooks/index.js +1 -1
- package/dist/hooks/ui/use-header-height.d.ts.map +1 -1
- package/dist/index.cjs +4 -4
- package/dist/index.js +3 -3
- package/package.json +1 -1
- package/src/components/features/time-tracker/time-tracker-header-button.tsx +1 -1
- package/src/components/navigation/app-header.tsx +10 -10
- package/src/components/navigation/header-button.tsx +4 -1
- package/src/components/navigation/header-mingo-button.tsx +2 -2
- package/src/components/navigation/header-skeleton.tsx +4 -4
- package/src/components/navigation/header.tsx +20 -14
- package/src/components/navigation/mingo-ai-button.tsx +23 -22
- package/src/components/navigation/mobile-burger-menu.tsx +3 -3
- package/src/components/navigation/top-navigation.tsx +17 -1
- package/src/components/ui/button/button.tsx +165 -133
- package/src/hooks/ui/use-header-height.ts +3 -1
- package/src/stories/TopNavigation.stories.tsx +20 -1
- package/dist/chunk-DMXB3VE2.cjs.map +0 -1
- package/dist/chunk-F7AD5CUU.js.map +0 -1
- package/dist/chunk-FFXNRJ5S.cjs.map +0 -1
- package/dist/chunk-L5CI57RM.cjs.map +0 -1
- package/dist/chunk-WHJGRRK2.js.map +0 -1
- /package/dist/{chunk-OAWFYZZT.js.map → chunk-32J4AXWO.js.map} +0 -0
- /package/dist/{chunk-HR6F6IP4.js.map → chunk-AFHWEJXM.js.map} +0 -0
- /package/dist/{chunk-VYEDTVIZ.js.map → chunk-CSRGIQ2G.js.map} +0 -0
- /package/dist/{chunk-4SR2QHMB.js.map → chunk-EFBKOCEA.js.map} +0 -0
- /package/dist/{chunk-BZF63JXP.js.map → chunk-GCVAZR52.js.map} +0 -0
- /package/dist/{chunk-FTOVZ7O2.js.map → chunk-HNDHCJV4.js.map} +0 -0
- /package/dist/{chunk-XV5SXALY.js.map → chunk-JCF55O7G.js.map} +0 -0
- /package/dist/{chunk-CLQEBWV7.js.map → chunk-JQDGP4CE.js.map} +0 -0
- /package/dist/{chunk-VO2ZONMR.js.map → chunk-MRPFFHBK.js.map} +0 -0
- /package/dist/{chunk-G43CEISB.js.map → chunk-QFQBYNQJ.js.map} +0 -0
- /package/dist/{chunk-SFJRMBJC.js.map → chunk-XRGAQM3F.js.map} +0 -0
- /package/dist/{chunk-VCPTU4IW.js.map → chunk-XT4P4JGJ.js.map} +0 -0
|
@@ -1,22 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
'use client';
|
|
2
2
|
|
|
3
|
-
import { Slot } from
|
|
4
|
-
import { cva, type VariantProps } from
|
|
5
|
-
import
|
|
6
|
-
import
|
|
3
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
4
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import Link from '../../../embed-shims/next-link';
|
|
7
7
|
|
|
8
|
-
import { cn } from
|
|
9
|
-
import {
|
|
8
|
+
import { cn } from '../../../utils/cn';
|
|
9
|
+
import {
|
|
10
|
+
buttonSurfaceClasses,
|
|
11
|
+
outlineBorderClasses,
|
|
12
|
+
splitDividerColorClasses,
|
|
13
|
+
splitGlyphSizeClasses,
|
|
14
|
+
} from './button-styles';
|
|
10
15
|
|
|
11
16
|
// Default layout: centered single content area, padding/gap on the button itself.
|
|
12
17
|
const buttonVariants = cva(
|
|
13
18
|
[
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
'relative inline-flex items-center justify-center gap-[var(--spacing-system-xsf)]',
|
|
20
|
+
'rounded-md whitespace-nowrap',
|
|
21
|
+
'transition-colors duration-200',
|
|
22
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ods-focus',
|
|
23
|
+
'disabled:pointer-events-none',
|
|
24
|
+
'[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:h-5 [&_svg]:w-5',
|
|
20
25
|
],
|
|
21
26
|
{
|
|
22
27
|
variants: {
|
|
@@ -30,9 +35,9 @@ const buttonVariants = cva(
|
|
|
30
35
|
overlay: buttonSurfaceClasses.overlay,
|
|
31
36
|
},
|
|
32
37
|
size: {
|
|
33
|
-
default:
|
|
34
|
-
small:
|
|
35
|
-
|
|
38
|
+
default: 'py-[var(--spacing-system-sf)] px-[var(--spacing-system-m)] text-h3 md:h-12 h-11',
|
|
39
|
+
small: 'p-[var(--spacing-system-xs)] text-h5 h-6 md:h-8',
|
|
40
|
+
'small-legacy': 'py-[var(--spacing-system-xs)] px-[var(--spacing-system-m)] h-10 text-[14px] font-bold', // Temporary alias for "small" — deprecated; grep size="small-legacy" (lib + hub) and migrate the remaining consumers before removal
|
|
36
41
|
// 24px pill for slim strips (announcement/promo bars, inline banner
|
|
37
42
|
// actions — Primer banner / Polaris banner / Vercel-bar convention).
|
|
38
43
|
// The label matches the strip's MESSAGE type ramp exactly (DM Sans
|
|
@@ -42,47 +47,59 @@ const buttonVariants = cva(
|
|
|
42
47
|
// mono-uppercase control style of "small" nor a heavier weight.
|
|
43
48
|
// Pinned h-6 across breakpoints (24px = the WCAG 2.5.8 target-size
|
|
44
49
|
// floor) with horizontal-dominant padding and a 14px glyph.
|
|
45
|
-
compact:
|
|
46
|
-
icon:
|
|
50
|
+
compact: 'py-0 px-[var(--spacing-system-sf)] text-h6 font-normal h-6 [&_svg]:h-3.5 [&_svg]:w-3.5',
|
|
51
|
+
icon: 'p-[var(--spacing-system-sf)] h-11 w-11 md:h-12 md:w-12 [&_svg]:h-4 [&_svg]:w-4 md:[&_svg]:h-6 md:[&_svg]:w-6',
|
|
47
52
|
// Quiet 32px icon target with a 16px glyph, fixed across breakpoints
|
|
48
53
|
// (Carbon ghost sm / Primer medium / shadcn icon-sm all pin 32px;
|
|
49
54
|
// ≥ the 24px WCAG 2.5.8 target floor). For icon actions that read as
|
|
50
55
|
// metadata rather than CTAs — author-page social rows, share rows.
|
|
51
56
|
// Pair with variant="transparent" for the ghost treatment.
|
|
52
|
-
|
|
57
|
+
'icon-sm': 'p-[var(--spacing-system-xxs)] h-8 w-8 [&_svg]:h-4 [&_svg]:w-4',
|
|
53
58
|
// Bare 56px media glyph (play / unmute over video). A size variant so
|
|
54
59
|
// hosts stop re-implementing a <button> to escape the 20px svg cap.
|
|
55
|
-
|
|
60
|
+
'icon-glyph': 'p-0 h-14 w-14 [&_svg]:h-14 [&_svg]:w-14',
|
|
61
|
+
},
|
|
62
|
+
// Label typography axis (Figma 133-740). `bold` is the classic h3-bold
|
|
63
|
+
// label; `regular` swaps the DEFAULT size's label to the h4 step
|
|
64
|
+
// (DM Sans 500, neutral tracking) — added for navigation buttons.
|
|
65
|
+
// `small` keeps its mono-uppercase h5 label on both fonts per the spec.
|
|
66
|
+
font: {
|
|
67
|
+
bold: '',
|
|
68
|
+
regular: '',
|
|
56
69
|
},
|
|
57
70
|
fullWidth: {
|
|
58
|
-
true:
|
|
59
|
-
false:
|
|
71
|
+
true: 'w-full',
|
|
72
|
+
false: '',
|
|
60
73
|
},
|
|
61
74
|
noPaddingX: {
|
|
62
|
-
true:
|
|
63
|
-
false:
|
|
75
|
+
true: 'px-0',
|
|
76
|
+
false: '',
|
|
64
77
|
},
|
|
65
78
|
},
|
|
66
79
|
compoundVariants: [
|
|
67
|
-
{ size:
|
|
80
|
+
{ size: 'small', class: '[&_svg]:h-4 [&_svg]:w-4' },
|
|
81
|
+
// Comes after the size classes, so cn()'s tailwind-merge resolves the
|
|
82
|
+
// ods-typography group in favour of text-h4.
|
|
83
|
+
{ size: 'default', font: 'regular', class: 'text-h4' },
|
|
68
84
|
],
|
|
69
85
|
defaultVariants: {
|
|
70
|
-
variant:
|
|
71
|
-
size:
|
|
86
|
+
variant: 'accent',
|
|
87
|
+
size: 'default',
|
|
88
|
+
font: 'bold',
|
|
72
89
|
fullWidth: false,
|
|
73
90
|
noPaddingX: false,
|
|
74
91
|
},
|
|
75
|
-
}
|
|
76
|
-
)
|
|
92
|
+
},
|
|
93
|
+
);
|
|
77
94
|
|
|
78
95
|
// Split layout (used when `splitIcon` is provided): outer button has no padding;
|
|
79
96
|
// inner slots own padding/gap so the divider can span full button height.
|
|
80
97
|
const splitShellVariants = cva(
|
|
81
98
|
[
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
99
|
+
'group relative inline-flex items-stretch overflow-hidden rounded-md whitespace-nowrap',
|
|
100
|
+
'transition-colors duration-200',
|
|
101
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ods-focus',
|
|
102
|
+
'disabled:pointer-events-none',
|
|
86
103
|
],
|
|
87
104
|
{
|
|
88
105
|
variants: {
|
|
@@ -94,65 +111,86 @@ const splitShellVariants = cva(
|
|
|
94
111
|
warning: buttonSurfaceClasses.warning,
|
|
95
112
|
},
|
|
96
113
|
size: {
|
|
97
|
-
default:
|
|
98
|
-
small:
|
|
114
|
+
default: 'h-12',
|
|
115
|
+
small: 'h-6 md:h-8 text-h5',
|
|
99
116
|
},
|
|
100
|
-
|
|
117
|
+
// Same label-typography axis as the normal layout — `regular` swaps the
|
|
118
|
+
// default size's h3-bold label for the h4 (DM Sans 500) step.
|
|
119
|
+
font: { bold: '', regular: '' },
|
|
120
|
+
fullWidth: { true: 'w-full', false: '' },
|
|
101
121
|
},
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
122
|
+
compoundVariants: [
|
|
123
|
+
{ size: 'default', font: 'bold', class: 'text-h3' },
|
|
124
|
+
{ size: 'default', font: 'regular', class: 'text-h4' },
|
|
125
|
+
],
|
|
126
|
+
defaultVariants: { variant: 'accent', size: 'default', font: 'bold', fullWidth: false },
|
|
127
|
+
},
|
|
128
|
+
);
|
|
105
129
|
|
|
106
130
|
const splitSlotVariants = cva(
|
|
107
131
|
// Glyph size lives per-size, not in the base: cva concatenates base + variant
|
|
108
132
|
// without tailwind-merge, so a base `[&_svg]:h-5` and a variant `[&_svg]:h-4`
|
|
109
133
|
// would both ship and the winner would come down to stylesheet order.
|
|
110
|
-
[
|
|
134
|
+
['inline-flex items-center justify-center', '[&_svg]:shrink-0'],
|
|
111
135
|
{
|
|
112
136
|
variants: {
|
|
113
137
|
slot: {
|
|
114
|
-
main:
|
|
115
|
-
icon:
|
|
138
|
+
main: 'gap-[var(--spacing-system-xsf)]',
|
|
139
|
+
icon: 'border-l',
|
|
116
140
|
},
|
|
117
|
-
size: { default: splitGlyphSizeClasses, small:
|
|
118
|
-
variant: { accent:
|
|
141
|
+
size: { default: splitGlyphSizeClasses, small: '[&_svg]:h-4 [&_svg]:w-4' },
|
|
142
|
+
variant: { accent: '', outline: '', transparent: '', destructive: '', warning: '' },
|
|
119
143
|
},
|
|
120
144
|
compoundVariants: [
|
|
121
|
-
{ slot:
|
|
122
|
-
{ slot:
|
|
123
|
-
{ slot:
|
|
124
|
-
{ slot:
|
|
125
|
-
{
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
{
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
145
|
+
{ slot: 'main', size: 'default', class: 'px-[var(--spacing-system-m)] py-[var(--spacing-system-sf)]' },
|
|
146
|
+
{ slot: 'main', size: 'small', class: 'px-[var(--spacing-system-xs)]' },
|
|
147
|
+
{ slot: 'icon', size: 'default', class: 'w-10' },
|
|
148
|
+
{ slot: 'icon', size: 'small', class: 'w-6 md:w-8' },
|
|
149
|
+
{
|
|
150
|
+
slot: 'icon',
|
|
151
|
+
variant: 'accent',
|
|
152
|
+
class: cn(
|
|
153
|
+
splitDividerColorClasses.accent,
|
|
154
|
+
'group-disabled:border-ods-disabled group-aria-disabled:border-ods-disabled',
|
|
155
|
+
),
|
|
156
|
+
},
|
|
157
|
+
{ slot: 'icon', variant: 'outline', class: splitDividerColorClasses.outline },
|
|
158
|
+
{ slot: 'icon', variant: 'transparent', class: splitDividerColorClasses.transparent },
|
|
159
|
+
{
|
|
160
|
+
slot: 'icon',
|
|
161
|
+
variant: 'destructive',
|
|
162
|
+
class: cn(
|
|
163
|
+
splitDividerColorClasses.destructive,
|
|
164
|
+
'group-disabled:border-ods-disabled group-aria-disabled:border-ods-disabled',
|
|
165
|
+
),
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
slot: 'icon',
|
|
169
|
+
variant: 'warning',
|
|
170
|
+
class: cn(
|
|
171
|
+
splitDividerColorClasses.warning,
|
|
172
|
+
'group-disabled:border-ods-disabled group-aria-disabled:border-ods-disabled',
|
|
173
|
+
),
|
|
174
|
+
},
|
|
139
175
|
],
|
|
140
|
-
defaultVariants: { slot:
|
|
141
|
-
}
|
|
142
|
-
)
|
|
176
|
+
defaultVariants: { slot: 'main', size: 'default', variant: 'accent' },
|
|
177
|
+
},
|
|
178
|
+
);
|
|
143
179
|
|
|
144
180
|
/** @deprecated Use `size="small"` instead. Temporary alias kept for backward compatibility; will be removed in the future. */
|
|
145
|
-
type DeprecatedButtonSize =
|
|
181
|
+
type DeprecatedButtonSize = 'small-legacy';
|
|
146
182
|
|
|
147
|
-
type ButtonSize =
|
|
183
|
+
type ButtonSize =
|
|
184
|
+
| Exclude<VariantProps<typeof buttonVariants>['size'], 'small-legacy' | null | undefined>
|
|
185
|
+
| DeprecatedButtonSize;
|
|
148
186
|
|
|
149
187
|
interface ButtonProps
|
|
150
188
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
151
|
-
Omit<VariantProps<typeof buttonVariants>,
|
|
152
|
-
asChild?: boolean
|
|
153
|
-
href?: string
|
|
154
|
-
openInNewTab?: boolean
|
|
155
|
-
prefetch?: boolean
|
|
189
|
+
Omit<VariantProps<typeof buttonVariants>, 'size'> {
|
|
190
|
+
asChild?: boolean;
|
|
191
|
+
href?: string;
|
|
192
|
+
openInNewTab?: boolean;
|
|
193
|
+
prefetch?: boolean;
|
|
156
194
|
/**
|
|
157
195
|
* Pre-resolved anchor bundle (from `useNavLink({ href, targetPlatform })`).
|
|
158
196
|
* When set, renders the Button as `<Link>` with `href` / `target` / `rel`
|
|
@@ -161,22 +199,22 @@ interface ButtonProps
|
|
|
161
199
|
* gymnastics. Wins over the separate `href` / `openInNewTab` props.
|
|
162
200
|
*/
|
|
163
201
|
linkProps?: {
|
|
164
|
-
href: string
|
|
165
|
-
target?: '_blank'
|
|
166
|
-
rel?: 'noopener noreferrer'
|
|
167
|
-
onClick?: React.MouseEventHandler<HTMLAnchorElement
|
|
168
|
-
} | null
|
|
169
|
-
leftIcon?: React.ReactNode
|
|
170
|
-
rightIcon?: React.ReactNode
|
|
202
|
+
href: string;
|
|
203
|
+
target?: '_blank';
|
|
204
|
+
rel?: 'noopener noreferrer';
|
|
205
|
+
onClick?: React.MouseEventHandler<HTMLAnchorElement>;
|
|
206
|
+
} | null;
|
|
207
|
+
leftIcon?: React.ReactNode;
|
|
208
|
+
rightIcon?: React.ReactNode;
|
|
171
209
|
/**
|
|
172
210
|
* Renders a vertical divider and trailing icon area inside the button.
|
|
173
211
|
* The whole button stays a single click target — the icon is decorative
|
|
174
212
|
* (`aria-hidden`). For two independent click targets, use `<SplitButton>`.
|
|
175
213
|
* Only honored when `size` is `"default"` or `"small"`.
|
|
176
214
|
*/
|
|
177
|
-
splitIcon?: React.ReactNode
|
|
178
|
-
loading?: boolean
|
|
179
|
-
size?: ButtonSize
|
|
215
|
+
splitIcon?: React.ReactNode;
|
|
216
|
+
loading?: boolean;
|
|
217
|
+
size?: ButtonSize;
|
|
180
218
|
}
|
|
181
219
|
|
|
182
220
|
const Spinner = () => (
|
|
@@ -188,13 +226,14 @@ const Spinner = () => (
|
|
|
188
226
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
189
227
|
/>
|
|
190
228
|
</svg>
|
|
191
|
-
)
|
|
229
|
+
);
|
|
192
230
|
|
|
193
231
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
194
232
|
{
|
|
195
233
|
className,
|
|
196
234
|
variant,
|
|
197
235
|
size,
|
|
236
|
+
font,
|
|
198
237
|
fullWidth,
|
|
199
238
|
noPaddingX,
|
|
200
239
|
asChild,
|
|
@@ -213,25 +252,22 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
|
213
252
|
},
|
|
214
253
|
ref,
|
|
215
254
|
) {
|
|
216
|
-
const isDisabled = disabled || loading
|
|
255
|
+
const isDisabled = disabled || loading;
|
|
217
256
|
|
|
218
257
|
// splitIcon is only supported for default/small sizes. With other sizes
|
|
219
258
|
// (icon, small-legacy) we silently fall back to the normal layout.
|
|
220
|
-
const useSplitLayout = !!splitIcon && (size ===
|
|
259
|
+
const useSplitLayout = !!splitIcon && (size === 'default' || size === 'small' || size === undefined);
|
|
221
260
|
|
|
222
261
|
if (useSplitLayout) {
|
|
223
|
-
const safeSize = (size ??
|
|
224
|
-
const safeVariant = (variant ??
|
|
225
|
-
const shellClasses = cn(
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
)
|
|
229
|
-
const mainSlotClass = splitSlotVariants({ slot: "main", size: safeSize, variant: safeVariant })
|
|
230
|
-
const iconSlotClass = splitSlotVariants({ slot: "icon", size: safeSize, variant: safeVariant })
|
|
262
|
+
const safeSize = (size ?? 'default') as 'default' | 'small';
|
|
263
|
+
const safeVariant = (variant ?? 'accent') as 'accent' | 'outline' | 'transparent' | 'destructive' | 'warning';
|
|
264
|
+
const shellClasses = cn(splitShellVariants({ variant: safeVariant, size: safeSize, font, fullWidth }), className);
|
|
265
|
+
const mainSlotClass = splitSlotVariants({ slot: 'main', size: safeSize, variant: safeVariant });
|
|
266
|
+
const iconSlotClass = splitSlotVariants({ slot: 'icon', size: safeSize, variant: safeVariant });
|
|
231
267
|
|
|
232
268
|
const splitContent = (
|
|
233
269
|
<>
|
|
234
|
-
<span className={cn(
|
|
270
|
+
<span className={cn('contents', loading && 'invisible')}>
|
|
235
271
|
<span className={mainSlotClass}>
|
|
236
272
|
{leftIcon && <span className="inline-flex items-center">{leftIcon}</span>}
|
|
237
273
|
{children}
|
|
@@ -247,17 +283,21 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
|
247
283
|
</span>
|
|
248
284
|
)}
|
|
249
285
|
</>
|
|
250
|
-
)
|
|
286
|
+
);
|
|
251
287
|
|
|
252
288
|
// `linkProps` (the pre-resolved bundle from `useNavLink({ href, targetPlatform })`)
|
|
253
289
|
// wins over the legacy `href` + `openInNewTab` props so callers can thread
|
|
254
290
|
// the unified nav decision directly. Either path produces the same `<Link>`.
|
|
255
|
-
const splitAnchor =
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
291
|
+
const splitAnchor =
|
|
292
|
+
linkProps ??
|
|
293
|
+
(href
|
|
294
|
+
? {
|
|
295
|
+
href,
|
|
296
|
+
target: openInNewTab ? ('_blank' as const) : undefined,
|
|
297
|
+
rel: openInNewTab ? ('noopener noreferrer' as const) : undefined,
|
|
298
|
+
onClick: onClick as unknown as React.MouseEventHandler<HTMLAnchorElement> | undefined,
|
|
299
|
+
}
|
|
300
|
+
: null);
|
|
261
301
|
if (splitAnchor) {
|
|
262
302
|
return (
|
|
263
303
|
<Link
|
|
@@ -271,28 +311,22 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
|
271
311
|
aria-label={props['aria-label']}
|
|
272
312
|
aria-disabled={isDisabled || undefined}
|
|
273
313
|
tabIndex={isDisabled ? -1 : undefined}
|
|
274
|
-
className={cn(shellClasses, isDisabled &&
|
|
314
|
+
className={cn(shellClasses, isDisabled && 'pointer-events-none')}
|
|
275
315
|
onClick={splitAnchor.onClick}
|
|
276
316
|
>
|
|
277
317
|
{splitContent}
|
|
278
318
|
</Link>
|
|
279
|
-
)
|
|
319
|
+
);
|
|
280
320
|
}
|
|
281
321
|
|
|
282
322
|
return (
|
|
283
|
-
<button
|
|
284
|
-
ref={ref}
|
|
285
|
-
className={shellClasses}
|
|
286
|
-
disabled={isDisabled}
|
|
287
|
-
onClick={onClick}
|
|
288
|
-
{...props}
|
|
289
|
-
>
|
|
323
|
+
<button ref={ref} className={shellClasses} disabled={isDisabled} onClick={onClick} {...props}>
|
|
290
324
|
{splitContent}
|
|
291
325
|
</button>
|
|
292
|
-
)
|
|
326
|
+
);
|
|
293
327
|
}
|
|
294
328
|
|
|
295
|
-
const classes = cn(buttonVariants({ variant, size, fullWidth, noPaddingX }), className)
|
|
329
|
+
const classes = cn(buttonVariants({ variant, size, font, fullWidth, noPaddingX }), className);
|
|
296
330
|
|
|
297
331
|
// asChild: consumer fully controls the rendered element; we just stamp our classes.
|
|
298
332
|
if (asChild) {
|
|
@@ -300,14 +334,14 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
|
300
334
|
<Slot ref={ref} className={classes} {...props}>
|
|
301
335
|
{children}
|
|
302
336
|
</Slot>
|
|
303
|
-
)
|
|
337
|
+
);
|
|
304
338
|
}
|
|
305
339
|
|
|
306
340
|
// Real content stays in layout (preserving width) but goes invisible while loading.
|
|
307
341
|
// The spinner is absolutely positioned so it never shifts the button's size.
|
|
308
342
|
const content = (
|
|
309
343
|
<>
|
|
310
|
-
<span className={cn(
|
|
344
|
+
<span className={cn('contents', loading && 'invisible')}>
|
|
311
345
|
{leftIcon && <span className="inline-flex items-center">{leftIcon}</span>}
|
|
312
346
|
{children}
|
|
313
347
|
{rightIcon && <span className="inline-flex items-center">{rightIcon}</span>}
|
|
@@ -318,15 +352,19 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
|
318
352
|
</span>
|
|
319
353
|
)}
|
|
320
354
|
</>
|
|
321
|
-
)
|
|
355
|
+
);
|
|
322
356
|
|
|
323
357
|
// Same `linkProps`-wins-over-href resolution as the splitIcon branch.
|
|
324
|
-
const anchor =
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
358
|
+
const anchor =
|
|
359
|
+
linkProps ??
|
|
360
|
+
(href
|
|
361
|
+
? {
|
|
362
|
+
href,
|
|
363
|
+
target: openInNewTab ? ('_blank' as const) : undefined,
|
|
364
|
+
rel: openInNewTab ? ('noopener noreferrer' as const) : undefined,
|
|
365
|
+
onClick: onClick as unknown as React.MouseEventHandler<HTMLAnchorElement> | undefined,
|
|
366
|
+
}
|
|
367
|
+
: null);
|
|
330
368
|
if (anchor) {
|
|
331
369
|
return (
|
|
332
370
|
<Link
|
|
@@ -339,25 +377,19 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
|
|
339
377
|
aria-label={props['aria-label']}
|
|
340
378
|
aria-disabled={isDisabled || undefined}
|
|
341
379
|
tabIndex={isDisabled ? -1 : undefined}
|
|
342
|
-
className={cn(classes, isDisabled &&
|
|
380
|
+
className={cn(classes, isDisabled && 'pointer-events-none')}
|
|
343
381
|
onClick={anchor.onClick}
|
|
344
382
|
>
|
|
345
383
|
{content}
|
|
346
384
|
</Link>
|
|
347
|
-
)
|
|
385
|
+
);
|
|
348
386
|
}
|
|
349
387
|
|
|
350
388
|
return (
|
|
351
|
-
<button
|
|
352
|
-
ref={ref}
|
|
353
|
-
className={classes}
|
|
354
|
-
disabled={isDisabled}
|
|
355
|
-
onClick={onClick}
|
|
356
|
-
{...props}
|
|
357
|
-
>
|
|
389
|
+
<button ref={ref} className={classes} disabled={isDisabled} onClick={onClick} {...props}>
|
|
358
390
|
{content}
|
|
359
391
|
</button>
|
|
360
|
-
)
|
|
361
|
-
})
|
|
392
|
+
);
|
|
393
|
+
});
|
|
362
394
|
|
|
363
|
-
export { Button, buttonVariants, type ButtonProps }
|
|
395
|
+
export { Button, buttonVariants, type ButtonProps };
|
|
@@ -9,7 +9,9 @@ import { useEffect, useState } from 'react'
|
|
|
9
9
|
* overlap the header.
|
|
10
10
|
*/
|
|
11
11
|
export function useHeaderHeight(
|
|
12
|
-
|
|
12
|
+
// First-paint fallback = the unified top-navigation `big` bar (72px), the
|
|
13
|
+
// size every hub/marketing site renders; replaced by the live measurement.
|
|
14
|
+
defaultHeight = 72,
|
|
13
15
|
options: {
|
|
14
16
|
/**
|
|
15
17
|
* When false, no observers are attached and the default height is
|
|
@@ -23,7 +23,7 @@ const logo = (
|
|
|
23
23
|
const navLinks = (
|
|
24
24
|
<nav className="flex items-center gap-2" aria-label="Main navigation">
|
|
25
25
|
{['OpenFrame', 'OpenMSP', 'Resources', 'Pricing'].map(label => (
|
|
26
|
-
<Button key={label} variant="transparent" size="
|
|
26
|
+
<Button key={label} variant="transparent" size="default" font="regular">
|
|
27
27
|
{label}
|
|
28
28
|
</Button>
|
|
29
29
|
))}
|
|
@@ -79,6 +79,7 @@ const meta = {
|
|
|
79
79
|
tags: ['autodocs'],
|
|
80
80
|
argTypes: {
|
|
81
81
|
centerBreakpoint: { control: 'radio', options: ['md', 'lg'] },
|
|
82
|
+
size: { control: 'radio', options: ['small', 'big'] },
|
|
82
83
|
backgroundClassName: { control: 'text' },
|
|
83
84
|
mobileTopBorder: { control: 'boolean' },
|
|
84
85
|
},
|
|
@@ -115,6 +116,24 @@ export const MarketingFull: Story = {
|
|
|
115
116
|
},
|
|
116
117
|
};
|
|
117
118
|
|
|
119
|
+
/**
|
|
120
|
+
* The `big` (72px) bar every hub/marketing site renders — same zones, taller
|
|
121
|
+
* bar and 72px cells (Figma 2936-6812). `small` (56px) is the console size.
|
|
122
|
+
*/
|
|
123
|
+
export const MarketingBig: Story = {
|
|
124
|
+
args: {
|
|
125
|
+
...MarketingFull.args,
|
|
126
|
+
size: 'big',
|
|
127
|
+
// Big bar carries the full default-size CTA (Figma 2936-6812), not the
|
|
128
|
+
// 32px mono control.
|
|
129
|
+
cta: (
|
|
130
|
+
<Button variant="accent" onClick={fn()}>
|
|
131
|
+
Try for Free
|
|
132
|
+
</Button>
|
|
133
|
+
),
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
|
|
118
137
|
/**
|
|
119
138
|
* Console-style arrangement: no logo/nav (the app sidebar owns branding),
|
|
120
139
|
* `centerBreakpoint="md"` so the center zone (here just the spacer) starts at
|