@medplum/react 2.0.17 → 2.0.18
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/cjs/index.cjs +149 -84
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.min.cjs +1 -1
- package/dist/esm/AppShell/AppShell.mjs +1 -1
- package/dist/esm/AppShell/AppShell.mjs.map +1 -1
- package/dist/esm/AppShell/Navbar.mjs +21 -14
- package/dist/esm/AppShell/Navbar.mjs.map +1 -1
- package/dist/esm/BookmarkDialog/BookmarkDialog.mjs +49 -0
- package/dist/esm/BookmarkDialog/BookmarkDialog.mjs.map +1 -0
- package/dist/esm/DefaultResourceTimeline/DefaultResourceTimeline.mjs +6 -1
- package/dist/esm/DefaultResourceTimeline/DefaultResourceTimeline.mjs.map +1 -1
- package/dist/esm/PatientTimeline/PatientTimeline.mjs +10 -7
- package/dist/esm/PatientTimeline/PatientTimeline.mjs.map +1 -1
- package/dist/esm/SearchControl/SearchUtils.mjs +1 -0
- package/dist/esm/SearchControl/SearchUtils.mjs.map +1 -1
- package/dist/esm/SearchFilterEditor/SearchFilterEditor.mjs +1 -1
- package/dist/esm/SearchFilterEditor/SearchFilterEditor.mjs.map +1 -1
- package/dist/esm/ServiceRequestTimeline/ServiceRequestTimeline.mjs +7 -4
- package/dist/esm/ServiceRequestTimeline/ServiceRequestTimeline.mjs.map +1 -1
- package/dist/esm/auth/AuthenticationForm.mjs +1 -1
- package/dist/esm/auth/AuthenticationForm.mjs.map +1 -1
- package/dist/esm/index.min.mjs +1 -1
- package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconPlus.mjs +13 -0
- package/dist/esm/node_modules/@tabler/icons-react/dist/esm/icons/IconPlus.mjs.map +1 -0
- package/dist/types/AppShell/AppShell.d.ts +1 -0
- package/dist/types/AppShell/Navbar.d.ts +1 -0
- package/dist/types/BookmarkDialog/BookmarkDialog.d.ts +8 -0
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -680,6 +680,15 @@
|
|
|
680
680
|
["path", { d: "M14.5 4l5.5 5.5", key: "svg-3" }]
|
|
681
681
|
]);
|
|
682
682
|
|
|
683
|
+
/**
|
|
684
|
+
* @tabler/icons-react v2.17.0 - MIT
|
|
685
|
+
*/
|
|
686
|
+
|
|
687
|
+
var IconPlus = createReactComponent("plus", "IconPlus", [
|
|
688
|
+
["path", { d: "M12 5l0 14", key: "svg-0" }],
|
|
689
|
+
["path", { d: "M5 12l14 0", key: "svg-1" }]
|
|
690
|
+
]);
|
|
691
|
+
|
|
683
692
|
/**
|
|
684
693
|
* @tabler/icons-react v2.17.0 - MIT
|
|
685
694
|
*/
|
|
@@ -1390,6 +1399,102 @@
|
|
|
1390
1399
|
React.createElement(core$1.Text, { size: "xs", color: "dimmed", align: "center" }, props.version))))));
|
|
1391
1400
|
}
|
|
1392
1401
|
|
|
1402
|
+
/**
|
|
1403
|
+
* Parses an HTML form and returns the result as a JavaScript object.
|
|
1404
|
+
* @param form The HTML form element.
|
|
1405
|
+
*/
|
|
1406
|
+
function parseForm(form) {
|
|
1407
|
+
const result = {};
|
|
1408
|
+
for (const element of Array.from(form.elements)) {
|
|
1409
|
+
if (element instanceof HTMLInputElement) {
|
|
1410
|
+
parseInputElement(result, element);
|
|
1411
|
+
}
|
|
1412
|
+
else if (element instanceof HTMLTextAreaElement) {
|
|
1413
|
+
result[element.name] = element.value;
|
|
1414
|
+
}
|
|
1415
|
+
else if (element instanceof HTMLSelectElement) {
|
|
1416
|
+
parseSelectElement(result, element);
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
return result;
|
|
1420
|
+
}
|
|
1421
|
+
/**
|
|
1422
|
+
* Parses an HTML input element.
|
|
1423
|
+
* Sets the name/value pair in the result,
|
|
1424
|
+
* but only if the element is enabled and checked.
|
|
1425
|
+
* @param el The input element.
|
|
1426
|
+
* @param result The result builder.
|
|
1427
|
+
*/
|
|
1428
|
+
function parseInputElement(result, el) {
|
|
1429
|
+
if (el.disabled) {
|
|
1430
|
+
// Ignore disabled elements
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
if ((el.type === 'checkbox' || el.type === 'radio') && !el.checked) {
|
|
1434
|
+
// Ignore unchecked radio or checkbox elements
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
result[el.name] = el.value;
|
|
1438
|
+
}
|
|
1439
|
+
/**
|
|
1440
|
+
* Parses an HTML select element.
|
|
1441
|
+
* Sets the name/value pair if one is selected.
|
|
1442
|
+
* @param result The result builder.
|
|
1443
|
+
* @param el The select element.
|
|
1444
|
+
*/
|
|
1445
|
+
function parseSelectElement(result, el) {
|
|
1446
|
+
result[el.name] = el.value;
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
function Form(props) {
|
|
1450
|
+
return (React.createElement("form", { style: props.style, "data-testid": props.testid, onSubmit: (e) => {
|
|
1451
|
+
e.preventDefault();
|
|
1452
|
+
const formData = parseForm(e.target);
|
|
1453
|
+
if (props.onSubmit) {
|
|
1454
|
+
props.onSubmit(formData);
|
|
1455
|
+
}
|
|
1456
|
+
} }, props.children));
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
function BookmarkDialog(props) {
|
|
1460
|
+
const medplum = useMedplum();
|
|
1461
|
+
const config = medplum.getUserConfiguration();
|
|
1462
|
+
const location = reactRouterDom.useLocation();
|
|
1463
|
+
function submitHandler(formData) {
|
|
1464
|
+
const { menuname, bookmarkname: name } = formData;
|
|
1465
|
+
const target = location.pathname + location.search;
|
|
1466
|
+
const newConfig = core.deepClone(config);
|
|
1467
|
+
const menu = newConfig?.menu?.find(({ title }) => title === menuname);
|
|
1468
|
+
menu?.link?.push({ name, target });
|
|
1469
|
+
medplum
|
|
1470
|
+
.updateResource(newConfig)
|
|
1471
|
+
.then((res) => {
|
|
1472
|
+
// refresh current config menu
|
|
1473
|
+
config.menu = res.menu;
|
|
1474
|
+
medplum.dispatchEvent({ type: 'change' });
|
|
1475
|
+
notifications.showNotification({ color: 'green', message: 'Success' });
|
|
1476
|
+
props.onOk();
|
|
1477
|
+
})
|
|
1478
|
+
.catch((err) => {
|
|
1479
|
+
notifications.showNotification({ color: 'red', message: core.normalizeErrorString(err) });
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
return (React.createElement(core$1.Modal, { title: "Add Bookmark", closeButtonProps: { 'aria-label': 'Close' }, opened: props.visible, onClose: props.onCancel },
|
|
1483
|
+
React.createElement(Form, { onSubmit: submitHandler },
|
|
1484
|
+
React.createElement(core$1.Stack, null,
|
|
1485
|
+
React.createElement(SelectMenu, { config: config }),
|
|
1486
|
+
React.createElement(core$1.TextInput, { label: "Bookmark Name", type: "text", name: "bookmarkname", placeholder: "bookmark name", withAsterisk: true }),
|
|
1487
|
+
React.createElement(core$1.Group, { position: "right" },
|
|
1488
|
+
React.createElement(core$1.Button, { mt: "sm", type: "submit" }, "OK"))))));
|
|
1489
|
+
}
|
|
1490
|
+
function SelectMenu(props) {
|
|
1491
|
+
function userConfigToMenu(config) {
|
|
1492
|
+
return config?.menu?.map((menu) => menu.title);
|
|
1493
|
+
}
|
|
1494
|
+
const menus = userConfigToMenu(props.config);
|
|
1495
|
+
return (React.createElement(core$1.NativeSelect, { name: "menuname", defaultValue: menus?.[0], label: "Select Menu Option", placeholder: "Menu", data: menus, withAsterisk: true }));
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1393
1498
|
function toKey(element) {
|
|
1394
1499
|
return element.code;
|
|
1395
1500
|
}
|
|
@@ -1498,6 +1603,7 @@
|
|
|
1498
1603
|
function Navbar(props) {
|
|
1499
1604
|
const { classes } = useStyles$f();
|
|
1500
1605
|
const navigate = useMedplumNavigate();
|
|
1606
|
+
const [bookmarkDialogVisible, setBookmarkDialogVisible] = React.useState(false);
|
|
1501
1607
|
function onLinkClick(e, to) {
|
|
1502
1608
|
e.stopPropagation();
|
|
1503
1609
|
e.preventDefault();
|
|
@@ -1511,18 +1617,22 @@
|
|
|
1511
1617
|
navigate(`/${resourceType}`);
|
|
1512
1618
|
}
|
|
1513
1619
|
}
|
|
1514
|
-
return (React.createElement(
|
|
1515
|
-
React.createElement(core$1.Navbar
|
|
1516
|
-
React.createElement(
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
React.createElement(core$1.
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1620
|
+
return (React.createElement(React.Fragment, null,
|
|
1621
|
+
React.createElement(core$1.Navbar, { width: { sm: 250 }, p: "xs" },
|
|
1622
|
+
React.createElement(core$1.Navbar.Section, { mb: "sm" },
|
|
1623
|
+
React.createElement(CodeInput, { key: window.location.pathname, name: "resourceType", placeholder: "Resource Type", property: {
|
|
1624
|
+
binding: {
|
|
1625
|
+
valueSet: 'http://hl7.org/fhir/ValueSet/resource-types',
|
|
1626
|
+
},
|
|
1627
|
+
}, onChange: (newValue) => navigateResourceType(newValue), creatable: false, maxSelectedValues: 0, clearSearchOnChange: true, clearable: false })),
|
|
1628
|
+
React.createElement(core$1.Navbar.Section, { grow: true },
|
|
1629
|
+
props.menus?.map((menu) => (React.createElement(React.Fragment, { key: `menu-${menu.title}` },
|
|
1630
|
+
React.createElement(core$1.Text, { className: classes.menuTitle }, menu.title),
|
|
1631
|
+
menu.links?.map((link) => (React.createElement(NavbarLink, { key: link.href, to: link.href, onClick: (e) => onLinkClick(e, link.href) },
|
|
1632
|
+
React.createElement(NavLinkIcon, { to: link.href, icon: link.icon }),
|
|
1633
|
+
React.createElement("span", null, link.label))))))),
|
|
1634
|
+
props.displayAddBookmark && (React.createElement(core$1.Button, { variant: "subtle", size: "xs", mt: "xl", leftIcon: React.createElement(IconPlus, { size: "0.75rem" }), onClick: () => setBookmarkDialogVisible(true) }, "Add Bookmark")))),
|
|
1635
|
+
React.createElement(BookmarkDialog, { visible: bookmarkDialogVisible, onOk: () => setBookmarkDialogVisible(false), onCancel: () => setBookmarkDialogVisible(false) })));
|
|
1526
1636
|
}
|
|
1527
1637
|
function NavbarLink(props) {
|
|
1528
1638
|
const { classes, cx } = useStyles$f();
|
|
@@ -1575,7 +1685,7 @@
|
|
|
1575
1685
|
main: {
|
|
1576
1686
|
background: theme.colorScheme === 'dark' ? theme.colors.dark[8] : theme.colors.gray[0],
|
|
1577
1687
|
},
|
|
1578
|
-
}, padding: 0, fixed: true, header: profile && React.createElement(Header, { logo: props.logo, version: props.version, navbarToggle: toggleNavbar }), navbar: profile && navbarOpen ? React.createElement(Navbar, { menus: props.menus, closeNavbar: closeNavbar }) : undefined },
|
|
1688
|
+
}, padding: 0, fixed: true, header: profile && React.createElement(Header, { logo: props.logo, version: props.version, navbarToggle: toggleNavbar }), navbar: profile && navbarOpen ? (React.createElement(Navbar, { menus: props.menus, closeNavbar: closeNavbar, displayAddBookmark: props.displayAddBookmark })) : undefined },
|
|
1579
1689
|
React.createElement(ErrorBoundary, null,
|
|
1580
1690
|
React.createElement(React.Suspense, { fallback: React.createElement(Loading, null) }, props.children))));
|
|
1581
1691
|
}
|
|
@@ -3256,63 +3366,6 @@
|
|
|
3256
3366
|
return code === 'AA' || code === 'LL' || code === 'HH' || code === 'A';
|
|
3257
3367
|
}
|
|
3258
3368
|
|
|
3259
|
-
/**
|
|
3260
|
-
* Parses an HTML form and returns the result as a JavaScript object.
|
|
3261
|
-
* @param form The HTML form element.
|
|
3262
|
-
*/
|
|
3263
|
-
function parseForm(form) {
|
|
3264
|
-
const result = {};
|
|
3265
|
-
for (const element of Array.from(form.elements)) {
|
|
3266
|
-
if (element instanceof HTMLInputElement) {
|
|
3267
|
-
parseInputElement(result, element);
|
|
3268
|
-
}
|
|
3269
|
-
else if (element instanceof HTMLTextAreaElement) {
|
|
3270
|
-
result[element.name] = element.value;
|
|
3271
|
-
}
|
|
3272
|
-
else if (element instanceof HTMLSelectElement) {
|
|
3273
|
-
parseSelectElement(result, element);
|
|
3274
|
-
}
|
|
3275
|
-
}
|
|
3276
|
-
return result;
|
|
3277
|
-
}
|
|
3278
|
-
/**
|
|
3279
|
-
* Parses an HTML input element.
|
|
3280
|
-
* Sets the name/value pair in the result,
|
|
3281
|
-
* but only if the element is enabled and checked.
|
|
3282
|
-
* @param el The input element.
|
|
3283
|
-
* @param result The result builder.
|
|
3284
|
-
*/
|
|
3285
|
-
function parseInputElement(result, el) {
|
|
3286
|
-
if (el.disabled) {
|
|
3287
|
-
// Ignore disabled elements
|
|
3288
|
-
return;
|
|
3289
|
-
}
|
|
3290
|
-
if ((el.type === 'checkbox' || el.type === 'radio') && !el.checked) {
|
|
3291
|
-
// Ignore unchecked radio or checkbox elements
|
|
3292
|
-
return;
|
|
3293
|
-
}
|
|
3294
|
-
result[el.name] = el.value;
|
|
3295
|
-
}
|
|
3296
|
-
/**
|
|
3297
|
-
* Parses an HTML select element.
|
|
3298
|
-
* Sets the name/value pair if one is selected.
|
|
3299
|
-
* @param result The result builder.
|
|
3300
|
-
* @param el The select element.
|
|
3301
|
-
*/
|
|
3302
|
-
function parseSelectElement(result, el) {
|
|
3303
|
-
result[el.name] = el.value;
|
|
3304
|
-
}
|
|
3305
|
-
|
|
3306
|
-
function Form(props) {
|
|
3307
|
-
return (React.createElement("form", { style: props.style, "data-testid": props.testid, onSubmit: (e) => {
|
|
3308
|
-
e.preventDefault();
|
|
3309
|
-
const formData = parseForm(e.target);
|
|
3310
|
-
if (props.onSubmit) {
|
|
3311
|
-
props.onSubmit(formData);
|
|
3312
|
-
}
|
|
3313
|
-
} }, props.children));
|
|
3314
|
-
}
|
|
3315
|
-
|
|
3316
3369
|
const useStyles$9 = core$1.createStyles((theme, { width, fill }) => ({
|
|
3317
3370
|
paper: {
|
|
3318
3371
|
maxWidth: width,
|
|
@@ -3792,7 +3845,12 @@
|
|
|
3792
3845
|
|
|
3793
3846
|
function DefaultResourceTimeline(props) {
|
|
3794
3847
|
return (React.createElement(ResourceTimeline, { value: props.resource, loadTimelineResources: async (medplum, resourceType, id) => {
|
|
3795
|
-
|
|
3848
|
+
const ref = `${resourceType}/${id}`;
|
|
3849
|
+
const _count = 100;
|
|
3850
|
+
return Promise.allSettled([
|
|
3851
|
+
medplum.readHistory(resourceType, id),
|
|
3852
|
+
medplum.search('Task', { _filter: `based-on eq ${ref} or focus eq ${ref} or subject eq ${ref}`, _count }),
|
|
3853
|
+
]);
|
|
3796
3854
|
} }));
|
|
3797
3855
|
}
|
|
3798
3856
|
|
|
@@ -3910,6 +3968,7 @@
|
|
|
3910
3968
|
'of-type': 'of type',
|
|
3911
3969
|
missing: 'missing',
|
|
3912
3970
|
identifier: 'identifier',
|
|
3971
|
+
iterate: 'iterate',
|
|
3913
3972
|
};
|
|
3914
3973
|
/**
|
|
3915
3974
|
* Sets the array of filters.
|
|
@@ -4580,7 +4639,7 @@
|
|
|
4580
4639
|
return null;
|
|
4581
4640
|
}
|
|
4582
4641
|
const resourceType = props.search.resourceType;
|
|
4583
|
-
const searchParams = core.globalSchema.types[resourceType].searchParams;
|
|
4642
|
+
const searchParams = core.globalSchema.types[resourceType].searchParams ?? {};
|
|
4584
4643
|
const filters = search.filters || [];
|
|
4585
4644
|
return (React.createElement(core$1.Modal, { title: "Filters", closeButtonProps: { 'aria-label': 'Close' }, size: 900, opened: props.visible, onClose: props.onCancel },
|
|
4586
4645
|
React.createElement("div", null,
|
|
@@ -5327,15 +5386,18 @@
|
|
|
5327
5386
|
}
|
|
5328
5387
|
|
|
5329
5388
|
function PatientTimeline(props) {
|
|
5330
|
-
const loadTimelineResources = React.useCallback((medplum,
|
|
5389
|
+
const loadTimelineResources = React.useCallback((medplum, resourceType, id) => {
|
|
5390
|
+
const ref = `${resourceType}/${id}`;
|
|
5391
|
+
const _count = 100;
|
|
5331
5392
|
return Promise.allSettled([
|
|
5332
5393
|
medplum.readHistory('Patient', id),
|
|
5333
|
-
medplum.search('Communication',
|
|
5334
|
-
medplum.search('Device',
|
|
5335
|
-
medplum.search('DeviceRequest',
|
|
5336
|
-
medplum.search('DiagnosticReport',
|
|
5337
|
-
medplum.search('Media',
|
|
5338
|
-
medplum.search('ServiceRequest',
|
|
5394
|
+
medplum.search('Communication', { subject: ref, _count }),
|
|
5395
|
+
medplum.search('Device', { patient: ref, _count }),
|
|
5396
|
+
medplum.search('DeviceRequest', { patient: ref, _count }),
|
|
5397
|
+
medplum.search('DiagnosticReport', { subject: ref, _count }),
|
|
5398
|
+
medplum.search('Media', { subject: ref, _count }),
|
|
5399
|
+
medplum.search('ServiceRequest', { subject: ref, _count }),
|
|
5400
|
+
medplum.search('Task', { subject: ref, _count }),
|
|
5339
5401
|
]);
|
|
5340
5402
|
}, []);
|
|
5341
5403
|
return (React.createElement(ResourceTimeline, { value: props.patient, loadTimelineResources: loadTimelineResources, createCommunication: (resource, sender, text) => ({
|
|
@@ -6925,12 +6987,15 @@
|
|
|
6925
6987
|
}
|
|
6926
6988
|
|
|
6927
6989
|
function ServiceRequestTimeline(props) {
|
|
6928
|
-
return (React.createElement(ResourceTimeline, { value: props.serviceRequest, loadTimelineResources: async (medplum,
|
|
6990
|
+
return (React.createElement(ResourceTimeline, { value: props.serviceRequest, loadTimelineResources: async (medplum, resourceType, id) => {
|
|
6991
|
+
const ref = `${resourceType}/${id}`;
|
|
6992
|
+
const _count = 100;
|
|
6929
6993
|
return Promise.allSettled([
|
|
6930
6994
|
medplum.readHistory('ServiceRequest', id),
|
|
6931
|
-
medplum.search('Communication', 'based-on
|
|
6932
|
-
medplum.search('
|
|
6933
|
-
medplum.search('
|
|
6995
|
+
medplum.search('Communication', { 'based-on': ref, _count }),
|
|
6996
|
+
medplum.search('DiagnosticReport', { 'based-on': ref, _count }),
|
|
6997
|
+
medplum.search('Media', { 'based-on': ref, _count }),
|
|
6998
|
+
medplum.search('Task', { _filter: `based-on eq ${ref} or focus eq ${ref} or subject eq ${ref}`, _count }),
|
|
6934
6999
|
]);
|
|
6935
7000
|
}, createCommunication: (resource, sender, text) => ({
|
|
6936
7001
|
resourceType: 'Communication',
|
|
@@ -7240,7 +7305,7 @@
|
|
|
7240
7305
|
React.createElement(core$1.Center, { sx: { flexDirection: 'column' } }, children),
|
|
7241
7306
|
React.createElement(OperationOutcomeAlert, { issues: issues }),
|
|
7242
7307
|
React.createElement(core$1.Stack, { spacing: "xl" },
|
|
7243
|
-
React.createElement(core$1.PasswordInput, { name: "password", label: "Password", autoComplete: "off", required: true, error: getErrorsForInput(outcome, 'password') })),
|
|
7308
|
+
React.createElement(core$1.PasswordInput, { name: "password", label: "Password", autoComplete: "off", required: true, autoFocus: true, error: getErrorsForInput(outcome, 'password') })),
|
|
7244
7309
|
React.createElement(core$1.Group, { position: "apart", mt: "xl", spacing: 0, noWrap: true },
|
|
7245
7310
|
onForgotPassword && (React.createElement(core$1.Anchor, { component: "button", type: "button", color: "dimmed", onClick: onForgotPassword, size: "xs" }, "Forgot password")),
|
|
7246
7311
|
React.createElement(core$1.Checkbox, { id: "remember", name: "remember", label: "Remember me", size: "xs", sx: { lineHeight: 1 } }),
|