@ciromaciel/auth-react 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -1
- package/dist/index.esm.js +352 -129
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +350 -127
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2535,17 +2535,13 @@ function Wordmark({
|
|
|
2535
2535
|
});
|
|
2536
2536
|
}
|
|
2537
2537
|
|
|
2538
|
+
// Where the terms live. Hardcoded on purpose, like `API_BASE` in authSdk.js:
|
|
2539
|
+
// this ships inside the published bundle, and every screen of the seven panels
|
|
2540
|
+
// that links to the terms — the sign-in notice and the account card's footer —
|
|
2541
|
+
// must point at the same document. `termsUrl` overrides it for whoever hosts
|
|
2542
|
+
// their own.
|
|
2538
2543
|
const TERMS_URL = 'https://myinfrastructure.click/legal/terms';
|
|
2539
2544
|
|
|
2540
|
-
/**
|
|
2541
|
-
* The terms notice under the first step.
|
|
2542
|
-
*
|
|
2543
|
-
* It sits on step ONE and nowhere else: the account is born on the first
|
|
2544
|
-
* `verify` and there is no separate sign-up screen to put it on — this form IS
|
|
2545
|
-
* the sign-up for whoever has never entered. On step two the person already
|
|
2546
|
-
* agreed by asking for the code; repeating it there would only push the code
|
|
2547
|
-
* field down.
|
|
2548
|
-
*/
|
|
2549
2545
|
function TermsNotice({
|
|
2550
2546
|
url,
|
|
2551
2547
|
text,
|
|
@@ -3984,150 +3980,377 @@ function UserProfile({
|
|
|
3984
3980
|
});
|
|
3985
3981
|
}
|
|
3986
3982
|
|
|
3983
|
+
const ROLE_LABELS = {
|
|
3984
|
+
owner: 'Dono',
|
|
3985
|
+
admin: 'Administrador',
|
|
3986
|
+
member: 'Membro'
|
|
3987
|
+
};
|
|
3988
|
+
|
|
3989
|
+
/**
|
|
3990
|
+
* @typedef {Object} UserInformationItem
|
|
3991
|
+
* @property {string} [id] - Stable key
|
|
3992
|
+
* @property {string} label - The row's text
|
|
3993
|
+
* @property {Function} [icon] - A Tabler icon component
|
|
3994
|
+
* @property {Function} onClick
|
|
3995
|
+
*
|
|
3996
|
+
* @typedef {Object} UserInformationPlan
|
|
3997
|
+
* @property {string} [name] - "Pro", "Starter"… Shown as the badge
|
|
3998
|
+
* @property {number} [used] - How many of the plan's resources are in use
|
|
3999
|
+
* @property {number|null} [limit] - The plan's ceiling; `null` hides the meter
|
|
4000
|
+
* @property {string} [unit] - What is counted, in the plural: "projetos"
|
|
4001
|
+
* @property {string} [actionLabel='Ver planos']
|
|
4002
|
+
* @property {Function} [onClick] - Opens the plans
|
|
4003
|
+
*
|
|
4004
|
+
* @typedef {Object} UserInformationOrganization
|
|
4005
|
+
* @property {string} name
|
|
4006
|
+
* @property {string} [role] - `owner`, `admin`, `member`, or already a label
|
|
4007
|
+
*/
|
|
4008
|
+
|
|
4009
|
+
/**
|
|
4010
|
+
* @param {Object} props
|
|
4011
|
+
* @param {Object} props.user - The signed-in user (`name`, `email`, `image`)
|
|
4012
|
+
* @param {Function} props.signOut
|
|
4013
|
+
* @param {Function} [props.onAccountClick]
|
|
4014
|
+
* @param {Function} [props.onBillingClick]
|
|
4015
|
+
* @param {UserInformationItem[]} [props.items] - The panel's own rows, shown in their own group
|
|
4016
|
+
* @param {UserInformationPlan} [props.plan] - The plan strip; omitted, the strip is not drawn
|
|
4017
|
+
* @param {UserInformationOrganization} [props.organization] - Where the person is acting
|
|
4018
|
+
* @param {boolean} [props.branded=true] - The footer: "Protegido por Auth" and the terms link
|
|
4019
|
+
* @param {string|null} [props.termsUrl] - Where "Termos" points; `null` removes the link
|
|
4020
|
+
* @param {string} [props.termsLabel='Termos']
|
|
4021
|
+
* @param {string} [props.accountLabel='Conta']
|
|
4022
|
+
* @param {string} [props.billingLabel='Assinatura']
|
|
4023
|
+
* @param {string} [props.signOutLabel='Sair']
|
|
4024
|
+
*/
|
|
3987
4025
|
function UserInformation({
|
|
3988
4026
|
user,
|
|
3989
4027
|
signOut,
|
|
3990
4028
|
onAccountClick,
|
|
3991
4029
|
onBillingClick,
|
|
4030
|
+
items = [],
|
|
4031
|
+
plan,
|
|
4032
|
+
organization,
|
|
4033
|
+
branded = true,
|
|
4034
|
+
termsUrl = TERMS_URL,
|
|
4035
|
+
termsLabel = 'Termos',
|
|
3992
4036
|
accountLabel = 'Conta',
|
|
3993
4037
|
billingLabel = 'Assinatura',
|
|
4038
|
+
signOutLabel = 'Sair',
|
|
4039
|
+
// Kept so existing calls keep working. The card has one density now: the
|
|
4040
|
+
// popover's. `padded={false}` still removes the outer padding.
|
|
3994
4041
|
padded = true,
|
|
3995
|
-
size
|
|
4042
|
+
size,
|
|
4043
|
+
// eslint-disable-line no-unused-vars -- accepted and ignored, see above
|
|
3996
4044
|
style,
|
|
3997
4045
|
...others
|
|
3998
4046
|
}) {
|
|
3999
4047
|
if (!user) return null;
|
|
4048
|
+
const name = (user.fullName || user.name || '').trim();
|
|
4049
|
+
const email = (user.primaryEmailAddress || user.email || '').trim();
|
|
4000
4050
|
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
const
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
md: 'sm',
|
|
4025
|
-
lg: 'md'
|
|
4026
|
-
};
|
|
4027
|
-
const widthMap = {
|
|
4028
|
-
sm: 280,
|
|
4029
|
-
md: 320,
|
|
4030
|
-
lg: 400
|
|
4031
|
-
};
|
|
4032
|
-
const name = user.fullName || user.name || 'User';
|
|
4033
|
-
const email = user.primaryEmailAddress || user.email || '';
|
|
4034
|
-
const initials = name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2);
|
|
4035
|
-
return /*#__PURE__*/jsxRuntime.jsx(core.Box, {
|
|
4036
|
-
p: padded ? gapMap[size] : 0,
|
|
4037
|
-
w: widthMap[size],
|
|
4051
|
+
/*
|
|
4052
|
+
* A name equal to the email's local part is not a name: it is what an
|
|
4053
|
+
* account born from a code gets by default, and showing it on top of the
|
|
4054
|
+
* email repeats the same fact. It counts as "no name".
|
|
4055
|
+
*/
|
|
4056
|
+
const hasRealName = Boolean(name) && name.toLowerCase() !== email.split('@')[0].toLowerCase() && name.toLowerCase() !== email.toLowerCase();
|
|
4057
|
+
const title = hasRealName ? name : email || name;
|
|
4058
|
+
const initials = hasRealName ? name.split(/\s+/).map(part => part[0]).join('').slice(0, 2).toUpperCase() : (email || name).charAt(0).toUpperCase();
|
|
4059
|
+
const baseRows = [onAccountClick && {
|
|
4060
|
+
id: 'account',
|
|
4061
|
+
label: accountLabel,
|
|
4062
|
+
icon: iconsReact.IconUser,
|
|
4063
|
+
onClick: onAccountClick
|
|
4064
|
+
}, onBillingClick && {
|
|
4065
|
+
id: 'billing',
|
|
4066
|
+
label: billingLabel,
|
|
4067
|
+
icon: iconsReact.IconCreditCard,
|
|
4068
|
+
onClick: onBillingClick
|
|
4069
|
+
}].filter(Boolean);
|
|
4070
|
+
const panelRows = items.filter(item => item && item.label && typeof item.onClick === 'function');
|
|
4071
|
+
return /*#__PURE__*/jsxRuntime.jsxs(core.Box, {
|
|
4072
|
+
w: 288,
|
|
4073
|
+
maw: "100%",
|
|
4038
4074
|
style: style,
|
|
4039
4075
|
...others,
|
|
4040
|
-
children: /*#__PURE__*/jsxRuntime.jsxs(core.
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4076
|
+
children: [/*#__PURE__*/jsxRuntime.jsxs(core.Group, {
|
|
4077
|
+
wrap: "nowrap",
|
|
4078
|
+
gap: 10,
|
|
4079
|
+
px: padded ? 16 : 0,
|
|
4080
|
+
py: 14,
|
|
4081
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(core.Avatar, {
|
|
4082
|
+
src: user.imageUrl || user.image,
|
|
4083
|
+
alt: "",
|
|
4084
|
+
size: 32,
|
|
4085
|
+
radius: 0,
|
|
4086
|
+
color: "gray.9",
|
|
4087
|
+
variant: "filled",
|
|
4088
|
+
styles: {
|
|
4089
|
+
root: {
|
|
4090
|
+
borderRadius: 0
|
|
4091
|
+
},
|
|
4092
|
+
placeholder: {
|
|
4093
|
+
fontSize: 12,
|
|
4094
|
+
fontWeight: 800
|
|
4095
|
+
}
|
|
4096
|
+
},
|
|
4097
|
+
children: initials
|
|
4098
|
+
}), /*#__PURE__*/jsxRuntime.jsxs(core.Box, {
|
|
4099
|
+
style: {
|
|
4100
|
+
flex: 1,
|
|
4101
|
+
minWidth: 0
|
|
4102
|
+
},
|
|
4103
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(core.Text, {
|
|
4104
|
+
fz: 13,
|
|
4105
|
+
fw: 800,
|
|
4106
|
+
c: "gray.9",
|
|
4107
|
+
lh: 1.3,
|
|
4108
|
+
truncate: "end",
|
|
4109
|
+
children: title
|
|
4110
|
+
}), hasRealName && email && /*#__PURE__*/jsxRuntime.jsx(core.Text, {
|
|
4111
|
+
fz: 12,
|
|
4112
|
+
fw: 500,
|
|
4113
|
+
c: "gray.5",
|
|
4114
|
+
lh: 1.4,
|
|
4115
|
+
truncate: "end",
|
|
4116
|
+
children: email
|
|
4117
|
+
})]
|
|
4118
|
+
})]
|
|
4119
|
+
}), (organization?.name || plan) && /*#__PURE__*/jsxRuntime.jsx(ContextStrip, {
|
|
4120
|
+
organization: organization,
|
|
4121
|
+
plan: plan,
|
|
4122
|
+
padded: padded
|
|
4123
|
+
}), /*#__PURE__*/jsxRuntime.jsxs(core.Box, {
|
|
4124
|
+
role: "menu",
|
|
4125
|
+
"aria-label": "Conta",
|
|
4126
|
+
onKeyDown: moveFocus,
|
|
4127
|
+
children: [baseRows.length > 0 && /*#__PURE__*/jsxRuntime.jsx(RowGroup, {
|
|
4128
|
+
rows: baseRows,
|
|
4129
|
+
hasDivider: !organization?.name && !plan
|
|
4130
|
+
}), panelRows.length > 0 && /*#__PURE__*/jsxRuntime.jsx(RowGroup, {
|
|
4131
|
+
rows: panelRows,
|
|
4132
|
+
hasDivider: true
|
|
4133
|
+
}), signOut && /*#__PURE__*/jsxRuntime.jsx(RowGroup, {
|
|
4134
|
+
rows: [{
|
|
4135
|
+
id: 'sign-out',
|
|
4136
|
+
label: signOutLabel,
|
|
4137
|
+
icon: iconsReact.IconLogout,
|
|
4138
|
+
onClick: signOut
|
|
4139
|
+
}],
|
|
4140
|
+
hasDivider: true
|
|
4141
|
+
})]
|
|
4142
|
+
}), branded && /*#__PURE__*/jsxRuntime.jsxs(core.Group, {
|
|
4143
|
+
justify: termsUrl ? 'space-between' : 'center',
|
|
4144
|
+
gap: 8,
|
|
4145
|
+
px: 16,
|
|
4146
|
+
py: 10,
|
|
4147
|
+
bg: "gray.1",
|
|
4148
|
+
style: {
|
|
4149
|
+
borderTop: '1px solid var(--mantine-color-gray-2)'
|
|
4150
|
+
},
|
|
4151
|
+
children: [/*#__PURE__*/jsxRuntime.jsxs(core.Text, {
|
|
4152
|
+
fz: 11,
|
|
4153
|
+
fw: 500,
|
|
4154
|
+
c: "gray.5",
|
|
4155
|
+
children: ["Protegido por", ' ', /*#__PURE__*/jsxRuntime.jsx(core.Text, {
|
|
4156
|
+
span: true,
|
|
4157
|
+
inherit: true,
|
|
4158
|
+
fw: 800,
|
|
4159
|
+
c: "gray.7",
|
|
4160
|
+
children: "Auth"
|
|
4161
|
+
})]
|
|
4162
|
+
}), termsUrl && /*#__PURE__*/jsxRuntime.jsx(core.Anchor, {
|
|
4163
|
+
href: termsUrl
|
|
4164
|
+
/*
|
|
4165
|
+
* A new tab, like the sign-in's notice: the card sits
|
|
4166
|
+
* over a working panel, and reading the terms must
|
|
4167
|
+
* not navigate away from it.
|
|
4168
|
+
*/,
|
|
4169
|
+
target: "_blank",
|
|
4170
|
+
rel: "noopener noreferrer",
|
|
4171
|
+
fz: 11,
|
|
4172
|
+
fw: 500,
|
|
4173
|
+
c: "gray.5",
|
|
4174
|
+
underline: "always",
|
|
4175
|
+
children: termsLabel
|
|
4176
|
+
})]
|
|
4177
|
+
})]
|
|
4178
|
+
});
|
|
4179
|
+
}
|
|
4180
|
+
|
|
4181
|
+
/** The organization, the role and the plan's usage, on the alternate surface. */
|
|
4182
|
+
function ContextStrip({
|
|
4183
|
+
organization,
|
|
4184
|
+
plan,
|
|
4185
|
+
padded
|
|
4186
|
+
}) {
|
|
4187
|
+
const role = organization?.role ? ROLE_LABELS[organization.role] || organization.role : null;
|
|
4188
|
+
const hasMeter = plan && Number.isFinite(plan.used) && Number.isFinite(plan.limit) && plan.limit > 0;
|
|
4189
|
+
const ratio = hasMeter ? Math.min(1, Math.max(0, plan.used / plan.limit)) : 0;
|
|
4190
|
+
return /*#__PURE__*/jsxRuntime.jsxs(core.Stack, {
|
|
4191
|
+
gap: 8,
|
|
4192
|
+
px: padded ? 16 : 0,
|
|
4193
|
+
py: 12,
|
|
4194
|
+
bg: "gray.1",
|
|
4195
|
+
style: {
|
|
4196
|
+
borderTop: '1px solid var(--mantine-color-gray-2)',
|
|
4197
|
+
borderBottom: '1px solid var(--mantine-color-gray-2)'
|
|
4198
|
+
},
|
|
4199
|
+
children: [(organization?.name || plan?.name) && /*#__PURE__*/jsxRuntime.jsxs(core.Group, {
|
|
4200
|
+
gap: 8,
|
|
4201
|
+
wrap: "nowrap",
|
|
4202
|
+
children: [organization?.name && /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
4203
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(iconsReact.IconBuilding, {
|
|
4204
|
+
size: 14,
|
|
4205
|
+
stroke: 1.5,
|
|
4206
|
+
style: {
|
|
4207
|
+
flex: 'none',
|
|
4208
|
+
color: 'var(--mantine-color-gray-5)'
|
|
4209
|
+
}
|
|
4210
|
+
}), /*#__PURE__*/jsxRuntime.jsx(core.Text, {
|
|
4211
|
+
fz: 12,
|
|
4212
|
+
fw: 800,
|
|
4213
|
+
c: "gray.9",
|
|
4214
|
+
truncate: "end",
|
|
4215
|
+
style: {
|
|
4216
|
+
minWidth: 0
|
|
4056
4217
|
},
|
|
4057
|
-
children:
|
|
4058
|
-
}), /*#__PURE__*/jsxRuntime.jsxs(core.
|
|
4218
|
+
children: organization.name
|
|
4219
|
+
}), role && /*#__PURE__*/jsxRuntime.jsxs(core.Text, {
|
|
4220
|
+
fz: 12,
|
|
4221
|
+
fw: 500,
|
|
4222
|
+
c: "gray.5",
|
|
4059
4223
|
style: {
|
|
4060
|
-
flex:
|
|
4061
|
-
overflow: 'hidden'
|
|
4224
|
+
flex: 'none'
|
|
4062
4225
|
},
|
|
4063
|
-
children: [
|
|
4064
|
-
size: fontSizeTitleMap[size],
|
|
4065
|
-
fw: 700,
|
|
4066
|
-
truncate: "end",
|
|
4067
|
-
c: "dark.9",
|
|
4068
|
-
lh: 1.1,
|
|
4069
|
-
children: name
|
|
4070
|
-
}), /*#__PURE__*/jsxRuntime.jsx(core.Text, {
|
|
4071
|
-
size: fontSizeEmailMap[size],
|
|
4072
|
-
c: "gray.5",
|
|
4073
|
-
truncate: "end",
|
|
4074
|
-
lh: 1.1,
|
|
4075
|
-
children: email
|
|
4076
|
-
})]
|
|
4077
|
-
}), /*#__PURE__*/jsxRuntime.jsx(core.ActionIcon, {
|
|
4078
|
-
size: btnSizeMap[size],
|
|
4079
|
-
onClick: signOut,
|
|
4080
|
-
children: /*#__PURE__*/jsxRuntime.jsx(iconsReact.IconLogout, {
|
|
4081
|
-
size: 16,
|
|
4082
|
-
stroke: 1.5
|
|
4083
|
-
})
|
|
4084
|
-
})]
|
|
4085
|
-
}), /*#__PURE__*/jsxRuntime.jsxs(core.Group, {
|
|
4086
|
-
grow: true,
|
|
4087
|
-
children: [/*#__PURE__*/jsxRuntime.jsx(core.Button, {
|
|
4088
|
-
variant: "default",
|
|
4089
|
-
size: btnSizeMap[size],
|
|
4090
|
-
leftSection: /*#__PURE__*/jsxRuntime.jsx(iconsReact.IconSettings, {
|
|
4091
|
-
size: 16,
|
|
4092
|
-
stroke: 1.5
|
|
4093
|
-
}),
|
|
4094
|
-
onClick: onAccountClick,
|
|
4095
|
-
children: accountLabel
|
|
4096
|
-
}), /*#__PURE__*/jsxRuntime.jsx(core.Button, {
|
|
4097
|
-
variant: "default",
|
|
4098
|
-
size: btnSizeMap[size],
|
|
4099
|
-
leftSection: /*#__PURE__*/jsxRuntime.jsx(iconsReact.IconCreditCard, {
|
|
4100
|
-
size: 16,
|
|
4101
|
-
stroke: 1.5
|
|
4102
|
-
}),
|
|
4103
|
-
onClick: onBillingClick,
|
|
4104
|
-
children: billingLabel
|
|
4226
|
+
children: ["\xB7 ", role]
|
|
4105
4227
|
})]
|
|
4228
|
+
}), plan?.name && /*#__PURE__*/jsxRuntime.jsx(core.Text, {
|
|
4229
|
+
component: "span",
|
|
4230
|
+
fz: 10,
|
|
4231
|
+
fw: 800,
|
|
4232
|
+
tt: "uppercase",
|
|
4233
|
+
lts: "1.5px",
|
|
4234
|
+
c: "white",
|
|
4235
|
+
bg: "gray.9",
|
|
4236
|
+
px: 6,
|
|
4237
|
+
lh: 1.6,
|
|
4238
|
+
ml: "auto",
|
|
4239
|
+
style: {
|
|
4240
|
+
flex: 'none'
|
|
4241
|
+
},
|
|
4242
|
+
children: plan.name
|
|
4243
|
+
})]
|
|
4244
|
+
}), hasMeter && /*#__PURE__*/jsxRuntime.jsxs(jsxRuntime.Fragment, {
|
|
4245
|
+
children: [/*#__PURE__*/jsxRuntime.jsx(core.Box, {
|
|
4246
|
+
h: 4,
|
|
4247
|
+
bg: "gray.2",
|
|
4248
|
+
role: "meter",
|
|
4249
|
+
"aria-valuemin": 0,
|
|
4250
|
+
"aria-valuemax": plan.limit,
|
|
4251
|
+
"aria-valuenow": plan.used,
|
|
4252
|
+
"aria-label": plan.unit ? `${plan.unit} em uso` : 'Uso do plano',
|
|
4253
|
+
children: /*#__PURE__*/jsxRuntime.jsx(core.Box, {
|
|
4254
|
+
h: "100%",
|
|
4255
|
+
w: `${ratio * 100}%`,
|
|
4256
|
+
bg: "gray.9"
|
|
4257
|
+
})
|
|
4106
4258
|
}), /*#__PURE__*/jsxRuntime.jsxs(core.Group, {
|
|
4107
|
-
justify: "
|
|
4108
|
-
gap:
|
|
4109
|
-
|
|
4259
|
+
justify: "space-between",
|
|
4260
|
+
gap: 8,
|
|
4261
|
+
wrap: "nowrap",
|
|
4110
4262
|
children: [/*#__PURE__*/jsxRuntime.jsx(core.Text, {
|
|
4111
|
-
|
|
4263
|
+
fz: 12,
|
|
4264
|
+
fw: 500,
|
|
4112
4265
|
c: "gray.6",
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
c: "dark.9",
|
|
4124
|
-
children: "Auth"
|
|
4125
|
-
})]
|
|
4266
|
+
children: `${plan.used} de ${plan.limit}${plan.unit ? ` ${plan.unit}` : ''}`
|
|
4267
|
+
}), plan.onClick && /*#__PURE__*/jsxRuntime.jsx(core.Anchor, {
|
|
4268
|
+
component: "button",
|
|
4269
|
+
type: "button",
|
|
4270
|
+
fz: 12,
|
|
4271
|
+
fw: 700,
|
|
4272
|
+
c: "gray.9",
|
|
4273
|
+
underline: "always",
|
|
4274
|
+
onClick: plan.onClick,
|
|
4275
|
+
children: plan.actionLabel || 'Ver planos'
|
|
4126
4276
|
})]
|
|
4127
4277
|
})]
|
|
4128
|
-
})
|
|
4278
|
+
})]
|
|
4279
|
+
});
|
|
4280
|
+
}
|
|
4281
|
+
function RowGroup({
|
|
4282
|
+
rows,
|
|
4283
|
+
hasDivider
|
|
4284
|
+
}) {
|
|
4285
|
+
return /*#__PURE__*/jsxRuntime.jsx(core.Stack, {
|
|
4286
|
+
gap: 0,
|
|
4287
|
+
p: 6,
|
|
4288
|
+
style: hasDivider ? {
|
|
4289
|
+
borderTop: '1px solid var(--mantine-color-gray-2)'
|
|
4290
|
+
} : undefined,
|
|
4291
|
+
children: rows.map(row => /*#__PURE__*/jsxRuntime.jsx(Row, {
|
|
4292
|
+
...row
|
|
4293
|
+
}, row.id || row.label))
|
|
4129
4294
|
});
|
|
4130
4295
|
}
|
|
4296
|
+
function Row({
|
|
4297
|
+
label,
|
|
4298
|
+
icon: Icon,
|
|
4299
|
+
onClick
|
|
4300
|
+
}) {
|
|
4301
|
+
return /*#__PURE__*/jsxRuntime.jsxs(core.UnstyledButton, {
|
|
4302
|
+
role: "menuitem",
|
|
4303
|
+
onClick: onClick,
|
|
4304
|
+
px: 10,
|
|
4305
|
+
py: 7,
|
|
4306
|
+
w: "100%",
|
|
4307
|
+
style: {
|
|
4308
|
+
display: 'flex',
|
|
4309
|
+
alignItems: 'center',
|
|
4310
|
+
gap: 10,
|
|
4311
|
+
borderRadius: 0
|
|
4312
|
+
}
|
|
4313
|
+
/*
|
|
4314
|
+
* Hover and keyboard focus share the same surface: a row reached
|
|
4315
|
+
* with the arrows must look exactly like the one under the mouse.
|
|
4316
|
+
*/,
|
|
4317
|
+
onMouseEnter: event => event.currentTarget.style.background = 'var(--mantine-color-gray-1)',
|
|
4318
|
+
onMouseLeave: event => event.currentTarget.style.background = 'transparent',
|
|
4319
|
+
onFocus: event => event.currentTarget.style.background = 'var(--mantine-color-gray-1)',
|
|
4320
|
+
onBlur: event => event.currentTarget.style.background = 'transparent',
|
|
4321
|
+
children: [Icon && /*#__PURE__*/jsxRuntime.jsx(Icon, {
|
|
4322
|
+
size: 16,
|
|
4323
|
+
stroke: 1.5,
|
|
4324
|
+
style: {
|
|
4325
|
+
flex: 'none',
|
|
4326
|
+
color: 'var(--mantine-color-gray-5)'
|
|
4327
|
+
}
|
|
4328
|
+
}), /*#__PURE__*/jsxRuntime.jsx(core.Text, {
|
|
4329
|
+
fz: 12,
|
|
4330
|
+
fw: 500,
|
|
4331
|
+
c: "gray.9",
|
|
4332
|
+
truncate: "end",
|
|
4333
|
+
children: label
|
|
4334
|
+
})]
|
|
4335
|
+
});
|
|
4336
|
+
}
|
|
4337
|
+
|
|
4338
|
+
/*
|
|
4339
|
+
* Arrow keys walk the rows, Home and End jump to the ends — the WAI-ARIA menu
|
|
4340
|
+
* pattern. Tab still leaves the card, so it never traps focus inside a popover
|
|
4341
|
+
* the panel owns.
|
|
4342
|
+
*/
|
|
4343
|
+
function moveFocus(event) {
|
|
4344
|
+
const keys = ['ArrowDown', 'ArrowUp', 'Home', 'End'];
|
|
4345
|
+
if (!keys.includes(event.key)) return;
|
|
4346
|
+
const rows = Array.from(event.currentTarget.querySelectorAll('[role="menuitem"]'));
|
|
4347
|
+
if (rows.length === 0) return;
|
|
4348
|
+
event.preventDefault();
|
|
4349
|
+
const current = rows.indexOf(document.activeElement);
|
|
4350
|
+
const last = rows.length - 1;
|
|
4351
|
+
const next = event.key === 'Home' ? 0 : event.key === 'End' ? last : event.key === 'ArrowDown' ? current < last ? current + 1 : 0 : current > 0 ? current - 1 : last;
|
|
4352
|
+
rows[next].focus();
|
|
4353
|
+
}
|
|
4131
4354
|
|
|
4132
4355
|
/**
|
|
4133
4356
|
* Renderiza children apenas quando o usuário está autenticado
|