@datalayer/core 1.0.21 → 1.0.23
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/lib/components/billing/BillableAccountSelect.d.ts +35 -0
- package/lib/components/billing/BillableAccountSelect.js +376 -0
- package/lib/components/billing/index.d.ts +1 -0
- package/lib/components/billing/index.js +5 -0
- package/lib/components/display/JupyterDialog.d.ts +8 -1
- package/lib/components/display/JupyterDialog.js +38 -5
- package/lib/components/index.d.ts +2 -0
- package/lib/components/index.js +2 -0
- package/lib/components/principal/Principal.d.ts +37 -0
- package/lib/components/principal/Principal.js +54 -0
- package/lib/components/principal/PrincipalAvatar.d.ts +10 -0
- package/lib/components/principal/PrincipalAvatar.js +55 -0
- package/lib/components/principal/PrincipalBadge.d.ts +20 -0
- package/lib/components/principal/PrincipalBadge.js +208 -0
- package/lib/components/principal/PrincipalBanner.d.ts +7 -0
- package/lib/components/principal/PrincipalBanner.js +84 -0
- package/lib/components/principal/PrincipalDetailsOverlay.d.ts +23 -0
- package/lib/components/principal/PrincipalDetailsOverlay.js +86 -0
- package/lib/components/principal/PrincipalSwitcherMenu.d.ts +14 -0
- package/lib/components/principal/PrincipalSwitcherMenu.js +287 -0
- package/lib/components/principal/index.d.ts +7 -0
- package/lib/components/principal/index.js +10 -0
- package/lib/components/runtimes/RuntimeLauncherDialog.d.ts +4 -0
- package/lib/components/runtimes/RuntimeLauncherDialog.js +36 -11
- package/lib/components/runtimes/RuntimePickerCell.js +1 -1
- package/lib/components/runtimes/RuntimePickerNotebook.js +89 -24
- package/lib/components/runtimes/RuntimeReservationControl.js +13 -4
- package/lib/components/sharing/ShareAccessComponent.d.ts +18 -0
- package/lib/components/sharing/ShareAccessComponent.js +1368 -0
- package/lib/components/sharing/SharingEditor.d.ts +20 -0
- package/lib/components/sharing/SharingEditor.js +65 -0
- package/lib/components/sharing/index.d.ts +9 -0
- package/lib/components/sharing/index.js +12 -0
- package/lib/hooks/index.d.ts +5 -0
- package/lib/hooks/index.js +5 -0
- package/lib/hooks/useBillableAccountStore.d.ts +31 -0
- package/lib/hooks/useBillableAccountStore.js +53 -0
- package/lib/hooks/usePrincipalStore.d.ts +41 -0
- package/lib/hooks/usePrincipalStore.js +101 -0
- package/lib/hooks/useSelectedBillableAccount.d.ts +14 -0
- package/lib/hooks/useSelectedBillableAccount.js +36 -0
- package/lib/hooks/useSelectedPrincipal.d.ts +17 -0
- package/lib/hooks/useSelectedPrincipal.js +29 -0
- package/lib/hooks/useUsageRefreshStore.d.ts +8 -0
- package/lib/hooks/useUsageRefreshStore.js +16 -0
- package/lib/stateful/runtimes/actions.js +7 -1
- package/lib/utils/Handles.d.ts +3 -0
- package/lib/utils/Handles.js +53 -0
- package/lib/utils/index.d.ts +1 -0
- package/lib/utils/index.js +1 -0
- package/package.json +1 -3
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
7
|
+
import { ActionList, ActionMenu, Box, Text } from '@primer/react';
|
|
8
|
+
import { OrganizationIcon, PeopleIcon, PersonIcon, } from '@primer/octicons-react';
|
|
9
|
+
import { useCache, useAuthorization } from '../../hooks/index.js';
|
|
10
|
+
import { useCoreStore } from '../../state/index.js';
|
|
11
|
+
import { useIAMStore } from '../../state/substates/index.js';
|
|
12
|
+
import { memberships as fetchMemberships } from '../../api/iam/profile.js';
|
|
13
|
+
import { usePrincipalStore } from '../../hooks/usePrincipalStore.js';
|
|
14
|
+
import { useBillableAccountStore } from '../../hooks/useBillableAccountStore.js';
|
|
15
|
+
import { useSelectedPrincipal } from '../../hooks/useSelectedPrincipal.js';
|
|
16
|
+
import { formatFriendlyHandle } from '../../utils/Handles.js';
|
|
17
|
+
function truncatePrincipalLabel(label, maxChars) {
|
|
18
|
+
const trimmed = (label || '').trim();
|
|
19
|
+
if (!trimmed) {
|
|
20
|
+
return '';
|
|
21
|
+
}
|
|
22
|
+
if (trimmed.length <= maxChars) {
|
|
23
|
+
return trimmed;
|
|
24
|
+
}
|
|
25
|
+
return `${trimmed.slice(0, Math.max(0, maxChars - 1))}…`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* PrincipalSwitcherMenu — the *only* component allowed to write to the
|
|
29
|
+
* principal store and the billable account store. It keeps both stores in
|
|
30
|
+
* sync per the rule:
|
|
31
|
+
* - selecting a user/org principal → billable account = same user/org
|
|
32
|
+
* - selecting a team principal → billable account = the team's parent org
|
|
33
|
+
*/
|
|
34
|
+
export function PrincipalSwitcherMenu({ maxLabelChars = 48, fullWidth = true, showClosedBorder = true, }) {
|
|
35
|
+
const { user, token, iamRunUrl } = useIAMStore();
|
|
36
|
+
const { configuration } = useCoreStore();
|
|
37
|
+
const { checkIsPlatformAdmin } = useAuthorization();
|
|
38
|
+
const { useUserOrganizations } = useCache();
|
|
39
|
+
const organizationsQuery = useUserOrganizations();
|
|
40
|
+
const organizations = organizationsQuery.data || [];
|
|
41
|
+
const isOrganizationsLoading = organizationsQuery.isLoading;
|
|
42
|
+
const isPlatformAdmin = user ? checkIsPlatformAdmin(user) : false;
|
|
43
|
+
const [teams, setTeams] = useState([]);
|
|
44
|
+
const [teamsLoading, setTeamsLoading] = useState(false);
|
|
45
|
+
const selectUserPrincipal = usePrincipalStore(state => state.selectUserPrincipal);
|
|
46
|
+
const selectOrganizationPrincipal = usePrincipalStore(state => state.selectOrganizationPrincipal);
|
|
47
|
+
const selectTeamPrincipal = usePrincipalStore(state => state.selectTeamPrincipal);
|
|
48
|
+
const setBillableAccount = useBillableAccountStore(state => state.setBillableAccount);
|
|
49
|
+
const { selectedPrincipalKind, selectedPrincipalUid, selectedPrincipalHandle, selectedTeamParentOrganizationHandle, } = useSelectedPrincipal();
|
|
50
|
+
const personalUid = user?.uid || user?.id || '';
|
|
51
|
+
const personalHandle = user?.handle || '';
|
|
52
|
+
const selectUser = (uid, handle) => {
|
|
53
|
+
selectUserPrincipal(uid, handle);
|
|
54
|
+
setBillableAccount({ kind: 'user', uid, handle });
|
|
55
|
+
};
|
|
56
|
+
const selectOrganization = (uid, handle) => {
|
|
57
|
+
selectOrganizationPrincipal(uid, handle);
|
|
58
|
+
setBillableAccount({ kind: 'organization', uid, handle });
|
|
59
|
+
};
|
|
60
|
+
const selectTeam = (team, orgHandle) => {
|
|
61
|
+
if (!team.organizationUid) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
selectTeamPrincipal({
|
|
65
|
+
teamUid: team.uid,
|
|
66
|
+
teamHandle: team.handle,
|
|
67
|
+
organizationUid: team.organizationUid,
|
|
68
|
+
organizationHandle: orgHandle,
|
|
69
|
+
});
|
|
70
|
+
setBillableAccount({
|
|
71
|
+
kind: 'organization',
|
|
72
|
+
uid: team.organizationUid,
|
|
73
|
+
handle: orgHandle,
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
const getOrganizationUid = (organization) => String(organization?.uid || organization?.id || '');
|
|
77
|
+
const selectedOrganization = useMemo(() => organizations.find((org) => getOrganizationUid(org) === selectedPrincipalUid), [organizations, selectedPrincipalUid]);
|
|
78
|
+
const selectedTeam = useMemo(() => teams.find(team => team.uid === selectedPrincipalUid), [teams, selectedPrincipalUid]);
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
let cancelled = false;
|
|
81
|
+
const loadTeams = async () => {
|
|
82
|
+
if (!token) {
|
|
83
|
+
setTeams([]);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
setTeamsLoading(true);
|
|
87
|
+
try {
|
|
88
|
+
const baseUrl = iamRunUrl || configuration.iamRunUrl;
|
|
89
|
+
const response = await fetchMemberships(token, baseUrl);
|
|
90
|
+
const rawMemberships = Array.isArray(response?.memberships)
|
|
91
|
+
? response.memberships
|
|
92
|
+
: [];
|
|
93
|
+
const mappedTeams = rawMemberships
|
|
94
|
+
.filter((membership) => membership?.type === 'team')
|
|
95
|
+
.map((membership) => ({
|
|
96
|
+
uid: String(membership?.uid || membership?.id || '').trim(),
|
|
97
|
+
handle: String(membership?.handle || '').trim(),
|
|
98
|
+
organizationUid: String(membership?.organization_uid || '').trim() || undefined,
|
|
99
|
+
organizationHandle: String(membership?.organization_handle ||
|
|
100
|
+
membership?.organization?.handle ||
|
|
101
|
+
'').trim() || undefined,
|
|
102
|
+
}))
|
|
103
|
+
.filter((team) => Boolean(team.uid && team.handle));
|
|
104
|
+
if (!cancelled) {
|
|
105
|
+
setTeams(mappedTeams);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
if (!cancelled) {
|
|
110
|
+
setTeams([]);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
if (!cancelled) {
|
|
115
|
+
setTeamsLoading(false);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
void loadTeams();
|
|
120
|
+
return () => {
|
|
121
|
+
cancelled = true;
|
|
122
|
+
};
|
|
123
|
+
}, [token, iamRunUrl, configuration.iamRunUrl]);
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (!personalUid || !personalHandle) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (!selectedPrincipalUid) {
|
|
129
|
+
selectUser(personalUid, personalHandle);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (selectedPrincipalKind === 'organization' && isOrganizationsLoading) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (selectedPrincipalKind === 'organization' && !selectedOrganization) {
|
|
136
|
+
selectUser(personalUid, personalHandle);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (selectedPrincipalKind === 'team' && teamsLoading) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (selectedPrincipalKind === 'team' && !selectedTeam) {
|
|
143
|
+
selectUser(personalUid, personalHandle);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
if (selectedPrincipalKind === 'user' &&
|
|
147
|
+
selectedPrincipalUid !== personalUid) {
|
|
148
|
+
selectUser(personalUid, personalHandle);
|
|
149
|
+
}
|
|
150
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
151
|
+
}, [
|
|
152
|
+
personalUid,
|
|
153
|
+
personalHandle,
|
|
154
|
+
selectedPrincipalUid,
|
|
155
|
+
selectedPrincipalKind,
|
|
156
|
+
isOrganizationsLoading,
|
|
157
|
+
teamsLoading,
|
|
158
|
+
selectedOrganization,
|
|
159
|
+
selectedTeam,
|
|
160
|
+
]);
|
|
161
|
+
const effectiveHandle = selectedPrincipalKind === 'organization'
|
|
162
|
+
? selectedOrganization?.handle ||
|
|
163
|
+
selectedPrincipalHandle ||
|
|
164
|
+
personalHandle
|
|
165
|
+
: selectedPrincipalKind === 'team'
|
|
166
|
+
? selectedTeam?.handle || selectedPrincipalHandle || personalHandle
|
|
167
|
+
: personalHandle;
|
|
168
|
+
const organizationHandleByUid = useMemo(() => {
|
|
169
|
+
const byUid = new Map();
|
|
170
|
+
for (const organization of organizations) {
|
|
171
|
+
const uid = getOrganizationUid(organization);
|
|
172
|
+
const handle = String(organization?.handle || '').trim();
|
|
173
|
+
if (uid && handle) {
|
|
174
|
+
byUid.set(uid, handle);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return byUid;
|
|
178
|
+
}, [organizations]);
|
|
179
|
+
const resolveTeamOrganizationHandle = (team) => {
|
|
180
|
+
if (!team) {
|
|
181
|
+
return '';
|
|
182
|
+
}
|
|
183
|
+
const directHandle = String(team.organizationHandle || '').trim();
|
|
184
|
+
if (directHandle) {
|
|
185
|
+
return directHandle;
|
|
186
|
+
}
|
|
187
|
+
const fromOrganizations = team.organizationUid
|
|
188
|
+
? organizationHandleByUid.get(team.organizationUid) || ''
|
|
189
|
+
: '';
|
|
190
|
+
return fromOrganizations.trim();
|
|
191
|
+
};
|
|
192
|
+
const effectiveOrganizationHandle = selectedPrincipalKind === 'team'
|
|
193
|
+
? resolveTeamOrganizationHandle(selectedTeam) ||
|
|
194
|
+
selectedTeamParentOrganizationHandle ||
|
|
195
|
+
''
|
|
196
|
+
: '';
|
|
197
|
+
const selectedPrincipalLabel = selectedPrincipalKind === 'team'
|
|
198
|
+
? `@${formatFriendlyHandle(effectiveOrganizationHandle || personalHandle || 'organization')}/${formatFriendlyHandle(effectiveHandle)}`
|
|
199
|
+
: `@${formatFriendlyHandle(effectiveHandle)}`;
|
|
200
|
+
const selectedPrincipalLabelClosed = truncatePrincipalLabel(selectedPrincipalLabel, maxLabelChars);
|
|
201
|
+
const isCurrentUserPrincipal = selectedPrincipalKind === 'user';
|
|
202
|
+
const selectedItemSx = {
|
|
203
|
+
bg: 'accent.subtle',
|
|
204
|
+
borderColor: 'accent.muted',
|
|
205
|
+
color: 'accent.fg',
|
|
206
|
+
fontWeight: 'semibold',
|
|
207
|
+
};
|
|
208
|
+
const adminBadgeSx = {
|
|
209
|
+
ml: 'auto',
|
|
210
|
+
px: 1,
|
|
211
|
+
py: '2px',
|
|
212
|
+
borderRadius: 999,
|
|
213
|
+
bg: 'attention.subtle',
|
|
214
|
+
color: 'attention.fg',
|
|
215
|
+
fontSize: 0,
|
|
216
|
+
fontWeight: 'semibold',
|
|
217
|
+
lineHeight: 1.2,
|
|
218
|
+
textTransform: 'lowercase',
|
|
219
|
+
};
|
|
220
|
+
return (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsxs(Box, { as: "button", type: "button", "aria-label": "Switch principal", sx: {
|
|
221
|
+
width: fullWidth ? '100%' : 'auto',
|
|
222
|
+
p: 2,
|
|
223
|
+
display: 'flex',
|
|
224
|
+
alignItems: 'center',
|
|
225
|
+
justifyContent: 'space-between',
|
|
226
|
+
gap: 2,
|
|
227
|
+
bg: showClosedBorder ? 'canvas.subtle' : 'transparent',
|
|
228
|
+
border: showClosedBorder ? '1px solid' : 'none',
|
|
229
|
+
borderColor: showClosedBorder ? 'border.default' : 'transparent',
|
|
230
|
+
borderRadius: 2,
|
|
231
|
+
cursor: 'pointer',
|
|
232
|
+
textAlign: 'left',
|
|
233
|
+
}, children: [_jsx(Box, { sx: {
|
|
234
|
+
display: 'inline-flex',
|
|
235
|
+
alignItems: 'center',
|
|
236
|
+
color: 'fg.muted',
|
|
237
|
+
flexShrink: 0,
|
|
238
|
+
}, children: selectedPrincipalKind === 'organization' ? (_jsx(OrganizationIcon, { size: 16 })) : selectedPrincipalKind === 'team' ? (_jsx(PeopleIcon, { size: 16 })) : (_jsx(PersonIcon, { size: 16 })) }), _jsx(Box, { sx: { minWidth: 0, flex: 1 }, children: _jsx(Text, { sx: {
|
|
239
|
+
display: 'block',
|
|
240
|
+
color: 'accent.fg',
|
|
241
|
+
fontWeight: 'semibold',
|
|
242
|
+
fontSize: 1,
|
|
243
|
+
whiteSpace: 'nowrap',
|
|
244
|
+
overflow: 'hidden',
|
|
245
|
+
textOverflow: 'ellipsis',
|
|
246
|
+
maxWidth: '100%',
|
|
247
|
+
}, children: selectedPrincipalLabelClosed }) }), isPlatformAdmin && isCurrentUserPrincipal ? (_jsx(Box, { sx: {
|
|
248
|
+
ml: 'auto',
|
|
249
|
+
flexShrink: 0,
|
|
250
|
+
display: 'inline-flex',
|
|
251
|
+
alignItems: 'center',
|
|
252
|
+
}, children: _jsx(Box, { as: "span", sx: adminBadgeSx, children: "admin" }) })) : null] }) }), _jsx(ActionMenu.Overlay, { width: "medium", children: _jsxs(ActionList, { children: [_jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "User" }), _jsxs(ActionList.Item, { disabled: isCurrentUserPrincipal, selected: isCurrentUserPrincipal, sx: isCurrentUserPrincipal ? selectedItemSx : undefined, onSelect: () => {
|
|
253
|
+
if (isCurrentUserPrincipal) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (personalUid && personalHandle) {
|
|
257
|
+
selectUser(personalUid, personalHandle);
|
|
258
|
+
}
|
|
259
|
+
}, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(PersonIcon, {}) }), "@", formatFriendlyHandle(personalHandle || 'me'), isPlatformAdmin ? (_jsx(ActionList.TrailingVisual, { children: _jsx(Box, { as: "span", sx: adminBadgeSx, children: "admin" }) })) : null] })] }), _jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Organizations" }), organizations.length === 0 ? (_jsx(ActionList.Item, { disabled: true, children: "No organizations" })) : (organizations.map((organization) => {
|
|
260
|
+
const organizationUid = getOrganizationUid(organization);
|
|
261
|
+
const isCurrentOrganizationPrincipal = selectedPrincipalKind === 'organization' &&
|
|
262
|
+
selectedPrincipalUid === organizationUid;
|
|
263
|
+
return (_jsxs(ActionList.Item, { disabled: isCurrentOrganizationPrincipal, selected: isCurrentOrganizationPrincipal, sx: isCurrentOrganizationPrincipal
|
|
264
|
+
? selectedItemSx
|
|
265
|
+
: undefined, onSelect: () => {
|
|
266
|
+
if (isCurrentOrganizationPrincipal) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (organizationUid && organization.handle) {
|
|
270
|
+
selectOrganization(organizationUid, organization.handle);
|
|
271
|
+
}
|
|
272
|
+
}, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(OrganizationIcon, {}) }), "@", organization.handle] }, organizationUid));
|
|
273
|
+
}))] }), _jsxs(ActionList.Group, { children: [_jsx(ActionList.GroupHeading, { children: "Teams" }), teams.length === 0 ? (_jsx(ActionList.Item, { disabled: true, children: "No teams" })) : (teams.map(team => {
|
|
274
|
+
const isCurrentTeamPrincipal = selectedPrincipalKind === 'team' &&
|
|
275
|
+
selectedPrincipalUid === team.uid;
|
|
276
|
+
const orgHandle = resolveTeamOrganizationHandle(team) ||
|
|
277
|
+
personalHandle ||
|
|
278
|
+
'organization';
|
|
279
|
+
return (_jsxs(ActionList.Item, { disabled: isCurrentTeamPrincipal, selected: isCurrentTeamPrincipal, sx: isCurrentTeamPrincipal ? selectedItemSx : undefined, onSelect: () => {
|
|
280
|
+
if (isCurrentTeamPrincipal) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
selectTeam(team, orgHandle);
|
|
284
|
+
}, children: [_jsx(ActionList.LeadingVisual, { children: _jsx(PeopleIcon, {}) }), "@", formatFriendlyHandle(orgHandle), "/", formatFriendlyHandle(team.handle)] }, team.uid));
|
|
285
|
+
}))] })] }) })] }));
|
|
286
|
+
}
|
|
287
|
+
export default PrincipalSwitcherMenu;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * from './Principal';
|
|
2
|
+
export * from './PrincipalAvatar';
|
|
3
|
+
export * from './PrincipalBadge';
|
|
4
|
+
export * from './PrincipalBanner';
|
|
5
|
+
export { buildPrincipalProfilePath, PrincipalDetailsOverlay, } from './PrincipalDetailsOverlay';
|
|
6
|
+
export type { PrincipalDetailsOverlayProps } from './PrincipalDetailsOverlay';
|
|
7
|
+
export * from './PrincipalSwitcherMenu';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
export * from './Principal.js';
|
|
6
|
+
export * from './PrincipalAvatar.js';
|
|
7
|
+
export * from './PrincipalBadge.js';
|
|
8
|
+
export * from './PrincipalBanner.js';
|
|
9
|
+
export { buildPrincipalProfilePath, PrincipalDetailsOverlay, } from './PrincipalDetailsOverlay.js';
|
|
10
|
+
export * from './PrincipalSwitcherMenu.js';
|
|
@@ -31,8 +31,9 @@ const NOT_AVAILABLE_RETRIES = 5;
|
|
|
31
31
|
* Start Runtime Launcher Dialog.
|
|
32
32
|
*/
|
|
33
33
|
export function RuntimeLauncherDialog(props) {
|
|
34
|
-
const { dialogTitle, kernelSnapshot, manager, onSubmit, markdownParser, sanitizer, upgradeSubscription, startRuntime = true, } = props;
|
|
34
|
+
const { dialogTitle, kernelSnapshot, manager, onSubmit, markdownParser, sanitizer, upgradeSubscription, submitLabel, startRuntime = true, } = props;
|
|
35
35
|
const hasExample = startRuntime === 'with-example';
|
|
36
|
+
const shouldStartRuntime = startRuntime !== 'defer';
|
|
36
37
|
const user = iamStore.getState().user;
|
|
37
38
|
const environments = manager.environments.get();
|
|
38
39
|
const { configuration } = useCoreStore();
|
|
@@ -49,7 +50,7 @@ export function RuntimeLauncherDialog(props) {
|
|
|
49
50
|
const jupyterReactStore = useJupyterReactStore();
|
|
50
51
|
const jupyterLabAdapter = jupyterReactStore.jupyterLabAdapter;
|
|
51
52
|
const [selection, setSelection] = useState((kernelSnapshot?.environment || environments[0]?.name) ?? '');
|
|
52
|
-
const [timeLimit, setTimeLimit] = useState(
|
|
53
|
+
const [timeLimit, setTimeLimit] = useState(10);
|
|
53
54
|
const [runtimeName, setRuntimeName] = useState(environments[0]?.runtime?.givenNameTemplate || environments[0]?.title || '');
|
|
54
55
|
// Whether the runtim name has been changed by the user or not
|
|
55
56
|
const [hasCustomRuntimeName, setHasCustomRuntimeName] = useState(false);
|
|
@@ -60,16 +61,36 @@ export function RuntimeLauncherDialog(props) {
|
|
|
60
61
|
const [flashLevel, setFlashLevel] = useState('danger');
|
|
61
62
|
const isMounted = useIsMounted();
|
|
62
63
|
useEffect(() => {
|
|
63
|
-
if (
|
|
64
|
+
if (shouldStartRuntime) {
|
|
64
65
|
refreshCredits();
|
|
65
66
|
}
|
|
66
|
-
}, [
|
|
67
|
+
}, [shouldStartRuntime]);
|
|
67
68
|
const spec = useMemo(() => environments.find(spec => spec.name === selection), [environments, selection]);
|
|
68
69
|
const description = spec?.description ?? '';
|
|
69
70
|
const burningRate = spec?.burning_rate ?? 1;
|
|
70
71
|
const creditsToMinutes = 1.0 / burningRate / 60.0;
|
|
71
|
-
const
|
|
72
|
-
|
|
72
|
+
const includedRuns = user?.subscription?.usage?.included_runs ??
|
|
73
|
+
user?.subscription?.included_runs;
|
|
74
|
+
const currentRuns = user?.subscription?.usage?.current_runs ??
|
|
75
|
+
user?.subscription?.current_runs ??
|
|
76
|
+
user?.subscription?.used_runs;
|
|
77
|
+
const hasKnownRunAllowance = typeof includedRuns === 'number';
|
|
78
|
+
const hasRemainingRuns = hasKnownRunAllowance &&
|
|
79
|
+
typeof currentRuns === 'number' &&
|
|
80
|
+
includedRuns > 0 &&
|
|
81
|
+
currentRuns < includedRuns;
|
|
82
|
+
const hasKnownCredits = typeof credits?.available === 'number';
|
|
83
|
+
const maxFromCredits = hasKnownCredits
|
|
84
|
+
? Math.floor((credits.available ?? 0) * creditsToMinutes)
|
|
85
|
+
: 10;
|
|
86
|
+
const effectiveMaxMinutes = hasKnownCredits && hasKnownRunAllowance && !hasRemainingRuns
|
|
87
|
+
? Math.max(1, maxFromCredits)
|
|
88
|
+
: Math.max(10, maxFromCredits > 0 ? maxFromCredits : 0);
|
|
89
|
+
const outOfCredits = shouldStartRuntime &&
|
|
90
|
+
hasKnownCredits &&
|
|
91
|
+
hasKnownRunAllowance &&
|
|
92
|
+
!hasRemainingRuns &&
|
|
93
|
+
((credits.available ?? 0) <= 0 || maxFromCredits < Number.EPSILON);
|
|
73
94
|
const handleSelectionChange = useCallback((e) => {
|
|
74
95
|
const selection = e.target.value;
|
|
75
96
|
setSelection(selection);
|
|
@@ -81,7 +102,7 @@ export function RuntimeLauncherDialog(props) {
|
|
|
81
102
|
const handleSubmitRuntime = useCallback(async () => {
|
|
82
103
|
if (selection) {
|
|
83
104
|
setError(undefined);
|
|
84
|
-
setWaitingForRuntime(
|
|
105
|
+
setWaitingForRuntime(shouldStartRuntime);
|
|
85
106
|
const spec = environments.find(s => s.name === selection);
|
|
86
107
|
const desc = {
|
|
87
108
|
name: selection,
|
|
@@ -99,7 +120,7 @@ export function RuntimeLauncherDialog(props) {
|
|
|
99
120
|
desc.params['capabilities'] = ['user_storage'];
|
|
100
121
|
}
|
|
101
122
|
let success = true;
|
|
102
|
-
if (
|
|
123
|
+
if (shouldStartRuntime) {
|
|
103
124
|
success = false;
|
|
104
125
|
let availableTrial = 1;
|
|
105
126
|
let retryDelay = NOT_AVAILABLE_INIT_RETRY;
|
|
@@ -178,6 +199,9 @@ export function RuntimeLauncherDialog(props) {
|
|
|
178
199
|
success = await startNewKernel();
|
|
179
200
|
}
|
|
180
201
|
if (success && isMounted()) {
|
|
202
|
+
if (!shouldStartRuntime) {
|
|
203
|
+
setWaitingForRuntime(false);
|
|
204
|
+
}
|
|
181
205
|
onSubmit(desc);
|
|
182
206
|
}
|
|
183
207
|
}
|
|
@@ -191,6 +215,7 @@ export function RuntimeLauncherDialog(props) {
|
|
|
191
215
|
openExample,
|
|
192
216
|
jupyterLabAdapter,
|
|
193
217
|
timeLimit,
|
|
218
|
+
shouldStartRuntime,
|
|
194
219
|
isMounted,
|
|
195
220
|
]);
|
|
196
221
|
const handleUserStorageChange = useCallback((e) => {
|
|
@@ -228,8 +253,8 @@ export function RuntimeLauncherDialog(props) {
|
|
|
228
253
|
{
|
|
229
254
|
buttonType: 'primary',
|
|
230
255
|
onClick: handleSubmitRuntime,
|
|
231
|
-
content: waitingForRuntime ? (_jsx(Spinner, { size: "small" })) : (startRuntime ?? true) ?
|
|
232
|
-
disabled: waitingForRuntime ||
|
|
256
|
+
content: waitingForRuntime ? (_jsx(Spinner, { size: "small" })) : ((submitLabel ?? ((startRuntime ?? true) ? 'Launch' : 'Assign'))),
|
|
257
|
+
disabled: waitingForRuntime || !selection || outOfCredits,
|
|
233
258
|
autoFocus: true,
|
|
234
259
|
},
|
|
235
260
|
], children: _jsxs(Box, { as: "form", onKeyDown: event => {
|
|
@@ -244,7 +269,7 @@ export function RuntimeLauncherDialog(props) {
|
|
|
244
269
|
? () => {
|
|
245
270
|
navigate(USAGE_ROUTE);
|
|
246
271
|
}
|
|
247
|
-
: undefined, disabled: outOfCredits, label: 'Time reservation', max:
|
|
272
|
+
: undefined, disabled: outOfCredits, label: 'Time reservation', max: effectiveMaxMinutes, time: timeLimit, burningRate: burningRate, onTimeChange: setTimeLimit, error: outOfCredits ? 'You must add credits to your account.' : undefined })), !configuration.whiteLabel && (_jsxs(FormControl, { layout: "horizontal", children: [_jsxs(FormControl.Label, { id: "user-storage-label", children: ["User storage", _jsx(Tooltip, { text: 'The runtime will be slower to start.', direction: "e", style: { marginLeft: 3 }, children: _jsx(IconButton, { icon: AlertIcon, "aria-label": "", variant: "invisible" }) })] }), _jsx(ToggleSwitch, { checked: userStorage, size: "small", onClick: handleUserStorageChange, "aria-labelledby": "user-storage-label" })] })), _jsxs(FormControl, { sx: { paddingTop: '10px' }, children: [_jsx(FormControl.Label, { children: "Runtime name" }), _jsx(TextInput, { name: "name", value: runtimeName, onChange: handleKernelNameChange, block: true })] }), hasExample &&
|
|
248
273
|
jupyterLabAdapter?.jupyterLab &&
|
|
249
274
|
!configuration.whiteLabel && (_jsxs(FormControl, { sx: { paddingTop: '10px' }, children: [_jsx(FormControl.Label, { id: "open-example-label", children: "Open example notebook" }), _jsx(ToggleSwitch, { disabled: !environments.find(spec => spec.name === selection)?.example, checked: openExample, size: "small", onClick: handleSwitchClick, "aria-labelledby": "open-example-label" })] })), error && (_jsx(FlashClosable, { variant: flashLevel, actions: navigate && upgradeSubscription && flashLevel === 'warning' ? (_jsx(Button, { onClick: handleUpgrade, title: 'Upgrade your subscription.', children: "Upgrade" })) : undefined, children: error }))] }) }));
|
|
250
275
|
}
|
|
@@ -99,6 +99,6 @@ export function RuntimePickerCell(props) {
|
|
|
99
99
|
const datalayerMeta = model.getMetadata('datalayer') ?? {};
|
|
100
100
|
return (_jsxs(_Fragment, { children: [_jsx(RuntimePickerBase, { display: "menu", filterRuntime: filterKernel, preference: preference, multiServiceManager: multiServiceManager, runtimeDesc: datalayerMeta.kernel ? datalayerMeta.kernel : undefined, setRuntimeDesc: setSelectedRuntimeDesc, variant: 'cell', preActions: _jsxs(ActionList.Item, { disabled: !multiServiceManager.remote, onSelect: setCell, selected: hasCellRuntime, title: !multiServiceManager.remote
|
|
101
101
|
? 'You are not connected with Datalayer.'
|
|
102
|
-
: 'Assign a new temporary Runtime on each Cell execution.', children: [_jsx(ActionList.LeadingVisual, { children: _jsx(CloudUploadIcon, {}) }), trans.__('Assign a Cell Runtime')] }), postActions: token || !logIn ? (_jsxs(_Fragment, { children: [RuntimeSnippetsFacade.supports(preference?.language ?? '') && (_jsx(ActionList.Item, { onSelect: openVariableDialog, disabled: !isForeign, title: trans.__('Define variables to transfer between the document kernel and the cell kernel.'), children: trans.__('Define Cell Variables Transfer') })), !configuration.whiteLabel && (_jsx(ActionList.Item, { onSelect: openSnippetDialog, disabled: snippets.length === 0, title: trans.__('Inject a code snippet at the end of the cell.'), children: trans.__('Inject Code Snippet') }))] })) : (_jsx(ActionList.Item, { onSelect: props.logIn, title: 'Connect to the Runtime provider.', children: _jsx(ExternalTokenSilentLogin, { message: "Connect to the Runtime provider" }) })), translator: translator }), isVariableDialogOpen && (_jsx(RuntimeCellVariablesDialog, { model: model, onClose: closeVariableDialog, preference: preference, sessionContext: sessionContext, translator: translator })), isSnippetDialogOpen && (_jsx(SnippetDialog, { language: language, model: model, snippets: snippets, onClose: closeSnippetDialog, markdownParser: markdownParser, sanitizer: sanitizer })), isKernelDialogOpen && (_jsx(RuntimeLauncherDialog, { manager: multiServiceManager.remote, onSubmit: onStartRemote, startRuntime:
|
|
102
|
+
: 'Assign a new temporary Runtime on each Cell execution.', children: [_jsx(ActionList.LeadingVisual, { children: _jsx(CloudUploadIcon, {}) }), trans.__('Assign a Cell Runtime')] }), postActions: token || !logIn ? (_jsxs(_Fragment, { children: [RuntimeSnippetsFacade.supports(preference?.language ?? '') && (_jsx(ActionList.Item, { onSelect: openVariableDialog, disabled: !isForeign, title: trans.__('Define variables to transfer between the document kernel and the cell kernel.'), children: trans.__('Define Cell Variables Transfer') })), !configuration.whiteLabel && (_jsx(ActionList.Item, { onSelect: openSnippetDialog, disabled: snippets.length === 0, title: trans.__('Inject a code snippet at the end of the cell.'), children: trans.__('Inject Code Snippet') }))] })) : (_jsx(ActionList.Item, { onSelect: props.logIn, title: 'Connect to the Runtime provider.', children: _jsx(ExternalTokenSilentLogin, { message: "Connect to the Runtime provider" }) })), translator: translator }), isVariableDialogOpen && (_jsx(RuntimeCellVariablesDialog, { model: model, onClose: closeVariableDialog, preference: preference, sessionContext: sessionContext, translator: translator })), isSnippetDialogOpen && (_jsx(SnippetDialog, { language: language, model: model, snippets: snippets, onClose: closeSnippetDialog, markdownParser: markdownParser, sanitizer: sanitizer })), isKernelDialogOpen && (_jsx(RuntimeLauncherDialog, { manager: multiServiceManager.remote, onSubmit: onStartRemote, startRuntime: true, submitLabel: "Assign", markdownParser: markdownParser, sanitizer: sanitizer }))] }));
|
|
103
103
|
}
|
|
104
104
|
export default RuntimePickerCell;
|
|
@@ -21,9 +21,9 @@ import { RuntimePickerBase } from './RuntimePickerBase.js';
|
|
|
21
21
|
export function RuntimePickerNotebook(props) {
|
|
22
22
|
const { multiServiceManager, sessionContext, setValue, translator } = props;
|
|
23
23
|
const { configuration } = useCoreStore();
|
|
24
|
-
const { credits, refreshCredits, token } = useIAMStore();
|
|
24
|
+
const { credits, refreshCredits, token, user } = useIAMStore();
|
|
25
25
|
const [selectedRuntimeDesc, setSelectedRuntimeDesc] = useState();
|
|
26
|
-
const [timeLimit, setTimeLimit] = useState(
|
|
26
|
+
const [timeLimit, setTimeLimit] = useState(10);
|
|
27
27
|
const [userStorage, setUserStorage] = useState(false);
|
|
28
28
|
const [canTransferFrom, setTransferFrom] = useState(false);
|
|
29
29
|
const [canTransferTo, setTransferTo] = useState(false);
|
|
@@ -112,31 +112,96 @@ export function RuntimePickerNotebook(props) {
|
|
|
112
112
|
setUserStorage(!userStorage);
|
|
113
113
|
}, [userStorage]);
|
|
114
114
|
useEffect(() => {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
115
|
+
const resolvedBurningRate = selectedRuntimeDesc?.burningRate ??
|
|
116
|
+
multiServiceManager.remote?.environments
|
|
117
|
+
.get()
|
|
118
|
+
.find(env => env.name === selectedRuntimeDesc?.name)?.burning_rate;
|
|
119
|
+
const includedRuns = user?.subscription?.usage?.included_runs ??
|
|
120
|
+
user?.subscription?.included_runs;
|
|
121
|
+
const currentRuns = user?.subscription?.usage?.current_runs ??
|
|
122
|
+
user?.subscription?.current_runs ??
|
|
123
|
+
user?.subscription?.used_runs;
|
|
124
|
+
const hasKnownRunAllowance = typeof includedRuns === 'number';
|
|
125
|
+
const hasRemainingRuns = hasKnownRunAllowance &&
|
|
126
|
+
typeof currentRuns === 'number' &&
|
|
127
|
+
includedRuns > 0 &&
|
|
128
|
+
currentRuns < includedRuns;
|
|
129
|
+
const hasKnownCredits = typeof credits?.available === 'number';
|
|
130
|
+
const maxMinutes = selectedRuntimeDesc?.location === 'remote' && resolvedBurningRate
|
|
131
|
+
? Math.floor((credits?.available ?? 0) / resolvedBurningRate / 60.0)
|
|
132
|
+
: undefined;
|
|
133
|
+
const effectiveTimeLimit = selectedRuntimeDesc?.location === 'remote'
|
|
134
|
+
? Math.max(1, Math.min(timeLimit, maxMinutes && maxMinutes > 0 ? maxMinutes : 10))
|
|
135
|
+
: timeLimit;
|
|
136
|
+
const creditsLimit = selectedRuntimeDesc?.location === 'remote' && resolvedBurningRate
|
|
137
|
+
? Math.min(effectiveTimeLimit, MAXIMAL_RUNTIME_TIME_RESERVATION_MINUTES) *
|
|
138
|
+
resolvedBurningRate *
|
|
119
139
|
60
|
|
120
140
|
: undefined;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
selectedVariables: toTransfer,
|
|
141
|
+
const requiresRuntimeStart = !!selectedRuntimeDesc && !selectedRuntimeDesc.kernelId;
|
|
142
|
+
if (requiresRuntimeStart && selectedRuntimeDesc.location === 'remote') {
|
|
143
|
+
if (!resolvedBurningRate || !Number.isFinite(resolvedBurningRate)) {
|
|
144
|
+
setValue({ runtime: null, selectedVariables: toTransfer });
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
if (hasKnownCredits &&
|
|
148
|
+
hasKnownRunAllowance &&
|
|
149
|
+
!hasRemainingRuns &&
|
|
150
|
+
(!creditsLimit || creditsLimit <= 0)) {
|
|
151
|
+
setValue({ runtime: null, selectedVariables: toTransfer });
|
|
152
|
+
return;
|
|
134
153
|
}
|
|
135
|
-
|
|
136
|
-
|
|
154
|
+
}
|
|
155
|
+
setValue({
|
|
156
|
+
runtime: selectedRuntimeDesc
|
|
157
|
+
? {
|
|
158
|
+
environmentName: ['browser', 'remote'].includes(selectedRuntimeDesc.location)
|
|
159
|
+
? `${selectedRuntimeDesc.location}-${selectedRuntimeDesc.name}`
|
|
160
|
+
: selectedRuntimeDesc.name,
|
|
161
|
+
id: selectedRuntimeDesc.kernelId,
|
|
162
|
+
creditsLimit,
|
|
163
|
+
capabilities: userStorage ? ['user_storage'] : undefined,
|
|
164
|
+
}
|
|
165
|
+
: null,
|
|
166
|
+
selectedVariables: toTransfer,
|
|
167
|
+
});
|
|
168
|
+
}, [
|
|
169
|
+
selectedRuntimeDesc,
|
|
170
|
+
userStorage,
|
|
171
|
+
toTransfer,
|
|
172
|
+
timeLimit,
|
|
173
|
+
multiServiceManager.remote,
|
|
174
|
+
credits?.available,
|
|
175
|
+
user,
|
|
176
|
+
]);
|
|
137
177
|
const { kernelPreference: { canStart }, } = sessionContext;
|
|
138
|
-
const
|
|
139
|
-
|
|
178
|
+
const resolvedBurningRate = selectedRuntimeDesc?.burningRate ??
|
|
179
|
+
multiServiceManager.remote?.environments
|
|
180
|
+
.get()
|
|
181
|
+
.find(env => env.name === selectedRuntimeDesc?.name)?.burning_rate;
|
|
182
|
+
const maxFromCredits = resolvedBurningRate
|
|
183
|
+
? Math.floor((credits?.available ?? 0) / resolvedBurningRate / 60.0)
|
|
184
|
+
: -1;
|
|
185
|
+
const includedRuns = user?.subscription?.usage?.included_runs ??
|
|
186
|
+
user?.subscription?.included_runs;
|
|
187
|
+
const currentRuns = user?.subscription?.usage?.current_runs ??
|
|
188
|
+
user?.subscription?.current_runs ??
|
|
189
|
+
user?.subscription?.used_runs;
|
|
190
|
+
const hasKnownRunAllowance = typeof includedRuns === 'number';
|
|
191
|
+
const hasRemainingRuns = hasKnownRunAllowance &&
|
|
192
|
+
typeof currentRuns === 'number' &&
|
|
193
|
+
includedRuns > 0 &&
|
|
194
|
+
currentRuns < includedRuns;
|
|
195
|
+
const hasKnownCredits = typeof credits?.available === 'number';
|
|
196
|
+
const effectiveMaxMinutes = selectedRuntimeDesc?.location === 'remote'
|
|
197
|
+
? hasKnownCredits && hasKnownRunAllowance && !hasRemainingRuns
|
|
198
|
+
? Math.max(1, maxFromCredits)
|
|
199
|
+
: Math.max(10, maxFromCredits > 0 ? maxFromCredits : 0)
|
|
200
|
+
: Math.max(1, maxFromCredits);
|
|
201
|
+
const outOfCredits = hasKnownCredits &&
|
|
202
|
+
hasKnownRunAllowance &&
|
|
203
|
+
!hasRemainingRuns &&
|
|
204
|
+
maxFromCredits < Number.EPSILON;
|
|
140
205
|
return (_jsxs(Box, { as: "form", className: "dla-Runtimes-picker", children: [_jsx(Box, { sx: { padding: 'var(--stack-padding-condensed) 0' }, children: _jsx(RuntimePickerBase, { display: "radio", disabled: canStart === false, preference: {
|
|
141
206
|
id: sessionContext.session?.id,
|
|
142
207
|
kernelDisplayName: sessionContext.kernelPreference.shouldStart
|
|
@@ -156,7 +221,7 @@ export function RuntimePickerNotebook(props) {
|
|
|
156
221
|
</Button>
|
|
157
222
|
*/
|
|
158
223
|
_jsx(_Fragment, {})) : (_jsx(ActionList.Item, { onSelect: props.logIn, title: 'Connect to Runtime provider.', children: _jsx(ExternalTokenSilentLogin, { message: "Connect to the Runtime provider" }) })) }) }), !selectedRuntimeDesc?.kernelId &&
|
|
159
|
-
selectedRuntimeDesc?.location === 'remote' && (_jsxs(_Fragment, { children: [_jsx(RuntimeReservationControl, { disabled: outOfCredits || selectedRuntimeDesc?.location !== 'remote', label: 'Time reservation', max:
|
|
224
|
+
selectedRuntimeDesc?.location === 'remote' && (_jsxs(_Fragment, { children: [_jsx(RuntimeReservationControl, { disabled: outOfCredits || selectedRuntimeDesc?.location !== 'remote', label: 'Time reservation', max: effectiveMaxMinutes, time: timeLimit, onTimeChange: setTimeLimit, error: outOfCredits && maxFromCredits >= 0
|
|
160
225
|
? 'You must add credits to your account.'
|
|
161
226
|
: timeLimit === 0
|
|
162
227
|
? 'You must set a time limit.'
|
|
@@ -18,7 +18,18 @@ export const MAXIMAL_RUNTIME_TIME_RESERVATION_MINUTES = 24 * 60;
|
|
|
18
18
|
*/
|
|
19
19
|
export function RuntimeReservationControl(props) {
|
|
20
20
|
const { addCredits, burningRate, disabled, error, label, max: maxProps, onTimeChange, time, } = props;
|
|
21
|
-
const max = Math.min(maxProps, MAXIMAL_RUNTIME_TIME_RESERVATION_MINUTES);
|
|
21
|
+
const max = Math.max(1, Math.min(maxProps, MAXIMAL_RUNTIME_TIME_RESERVATION_MINUTES));
|
|
22
|
+
const displayedTime = Number.isFinite(time)
|
|
23
|
+
? Math.min(max, Math.max(1, time))
|
|
24
|
+
: 1;
|
|
25
|
+
const handleTimeChange = (valueOrEvent) => {
|
|
26
|
+
const rawValue = typeof valueOrEvent === 'number'
|
|
27
|
+
? valueOrEvent
|
|
28
|
+
: parseFloat(valueOrEvent?.target?.value);
|
|
29
|
+
if (Number.isFinite(rawValue)) {
|
|
30
|
+
onTimeChange(Math.min(max, Math.max(1, rawValue)));
|
|
31
|
+
}
|
|
32
|
+
};
|
|
22
33
|
// Temporary workaround to not show disabled components.
|
|
23
34
|
const hidden = disabled;
|
|
24
35
|
return !hidden ? (_jsxs(FormControl, { disabled: disabled, sx: { paddingBottom: 'var(--stack-padding-condensed)' }, children: [_jsx(FormControl.Label, { children: label }), _jsxs(Box, { style: {
|
|
@@ -27,7 +38,5 @@ export function RuntimeReservationControl(props) {
|
|
|
27
38
|
gridTemplateColumns: 'max-content 1fr max-content max-content',
|
|
28
39
|
gridGap: 'var(--stack-gap-condensed)',
|
|
29
40
|
width: '100%',
|
|
30
|
-
}, children: [_jsx(Slider, { step: 1, min: 1, max: max, value:
|
|
31
|
-
onTimeChange(parseFloat(event.target.value));
|
|
32
|
-
} }), (max === 0 || max > Number.EPSILON) && (_jsxs(_Fragment, { children: [_jsxs(Text, { children: ["out of ", maxProps, " available minutes"] }), addCredits && (_jsx(IconButton, { icon: PlusIcon, "aria-label": "Add credits", onClick: () => addCredits() }))] }))] }), _jsxs(FormControl.Caption, { children: ["Maximum execution time that can be consumed by the runtime. It must be less than ", (MAXIMAL_RUNTIME_TIME_RESERVATION_MINUTES / 60).toFixed(0), ' ', "hours.", burningRate && (_jsxs(_Fragment, { children: [_jsx("br", {}), `With the current value, the runtime execution will consume at most ${(time * burningRate * 60).toFixed(2)} credits.`] }))] }), error && (_jsx(FormControl.Validation, { variant: "error", children: error }))] })) : (_jsx(_Fragment, {}));
|
|
41
|
+
}, children: [_jsx(Slider, { step: 1, min: 1, max: max, value: displayedTime, onChange: handleTimeChange, disabled: disabled, label: "", displayValue: false }), _jsx(TextInput, { type: "number", step: "1", min: "1", max: max, disabled: disabled, value: displayedTime.toFixed(2), onChange: handleTimeChange }), (max === 0 || max > Number.EPSILON) && (_jsxs(_Fragment, { children: [_jsxs(Text, { children: ["out of ", maxProps, " available minutes"] }), addCredits && (_jsx(IconButton, { icon: PlusIcon, "aria-label": "Add credits", onClick: () => addCredits() }))] }))] }), _jsxs(FormControl.Caption, { children: ["Maximum execution time that can be consumed by the runtime. It must be less than ", (MAXIMAL_RUNTIME_TIME_RESERVATION_MINUTES / 60).toFixed(0), ' ', "hours.", burningRate && (_jsxs(_Fragment, { children: [_jsx("br", {}), `With the current value, the runtime execution will consume at most ${(time * burningRate * 60).toFixed(2)} credits.`] }))] }), error && (_jsx(FormControl.Validation, { variant: "error", children: error }))] })) : (_jsx(_Fragment, {}));
|
|
33
42
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type ItemAccessLevel = 'view' | 'update' | 'execute';
|
|
2
|
+
type PrincipalKind = 'user' | 'team' | 'organization';
|
|
3
|
+
export type ShareAccessComponentProps = {
|
|
4
|
+
isOpen: boolean;
|
|
5
|
+
requestUrl?: string;
|
|
6
|
+
resourceLabel: string;
|
|
7
|
+
resourceName?: string;
|
|
8
|
+
resourceDescription?: string;
|
|
9
|
+
onSharingAccessRestrictedChange?: (restricted: boolean, message?: string) => void;
|
|
10
|
+
defaultAccessLevel?: ItemAccessLevel;
|
|
11
|
+
principalKinds?: readonly PrincipalKind[];
|
|
12
|
+
displayMode?: 'dialog' | 'inline';
|
|
13
|
+
onClose: () => void;
|
|
14
|
+
};
|
|
15
|
+
export declare function ShareAccessComponent({ isOpen, requestUrl, resourceLabel, resourceName, resourceDescription: _resourceDescription, onSharingAccessRestrictedChange, defaultAccessLevel, principalKinds, displayMode, onClose, }: ShareAccessComponentProps): JSX.Element | null;
|
|
16
|
+
export default ShareAccessComponent;
|
|
17
|
+
export declare const ShareAccessDialog: typeof ShareAccessComponent;
|
|
18
|
+
export type ShareAccessDialogProps = ShareAccessComponentProps;
|