@medplum/react 0.9.27 → 0.9.28

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.
Files changed (59) hide show
  1. package/dist/cjs/index.js +272 -153
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/index.min.js +1 -1
  4. package/dist/cjs/index.min.js.map +1 -1
  5. package/dist/cjs/styles.css +83 -79
  6. package/dist/esm/Autocomplete.d.ts +1 -0
  7. package/dist/esm/Autocomplete.js +4 -8
  8. package/dist/esm/Autocomplete.js.map +1 -1
  9. package/dist/esm/CodeableConceptDisplay.js +4 -2
  10. package/dist/esm/CodeableConceptDisplay.js.map +1 -1
  11. package/dist/esm/DateTimeInput.js +1 -3
  12. package/dist/esm/DateTimeInput.js.map +1 -1
  13. package/dist/esm/DiagnosticReportDisplay.js +2 -4
  14. package/dist/esm/DiagnosticReportDisplay.js.map +1 -1
  15. package/dist/esm/GoogleButton.js +2 -2
  16. package/dist/esm/GoogleButton.js.map +1 -1
  17. package/dist/esm/PlanDefinitionBuilder.js +108 -39
  18. package/dist/esm/PlanDefinitionBuilder.js.map +1 -1
  19. package/dist/esm/QuestionnaireForm.js +3 -0
  20. package/dist/esm/QuestionnaireForm.js.map +1 -1
  21. package/dist/esm/RequestGroupDisplay.js +2 -3
  22. package/dist/esm/RequestGroupDisplay.js.map +1 -1
  23. package/dist/esm/ResourceArrayInput.js +1 -2
  24. package/dist/esm/ResourceArrayInput.js.map +1 -1
  25. package/dist/esm/ResourceHistoryTable.js +2 -3
  26. package/dist/esm/ResourceHistoryTable.js.map +1 -1
  27. package/dist/esm/ResourceInput.d.ts +1 -0
  28. package/dist/esm/ResourceInput.js +4 -3
  29. package/dist/esm/ResourceInput.js.map +1 -1
  30. package/dist/esm/ResourcePropertyDisplay.js +5 -5
  31. package/dist/esm/ResourcePropertyDisplay.js.map +1 -1
  32. package/dist/esm/ResourcePropertyInput.js +3 -0
  33. package/dist/esm/ResourcePropertyInput.js.map +1 -1
  34. package/dist/esm/SearchFilterValueDisplay.js +2 -2
  35. package/dist/esm/SearchFilterValueDisplay.js.map +1 -1
  36. package/dist/esm/SearchUtils.js +2 -3
  37. package/dist/esm/SearchUtils.js.map +1 -1
  38. package/dist/esm/Timeline.js +2 -4
  39. package/dist/esm/Timeline.js.map +1 -1
  40. package/dist/esm/TimingInput.d.ts +8 -0
  41. package/dist/esm/TimingInput.js +90 -0
  42. package/dist/esm/TimingInput.js.map +1 -0
  43. package/dist/esm/index.d.ts +0 -1
  44. package/dist/esm/index.js +0 -1
  45. package/dist/esm/index.js.map +1 -1
  46. package/dist/esm/index.min.js +1 -1
  47. package/dist/esm/index.min.js.map +1 -1
  48. package/dist/esm/stories/Autocomplete.stories.d.ts +1 -0
  49. package/dist/esm/stories/CodeableConceptDisplay.stories.d.ts +8 -0
  50. package/dist/esm/stories/TimingInput.stories.d.ts +6 -0
  51. package/dist/esm/styles.css +83 -79
  52. package/package.json +5 -5
  53. package/stats.html +4034 -0
  54. package/dist/esm/DateTimeDisplay.d.ts +0 -5
  55. package/dist/esm/DateTimeDisplay.js +0 -11
  56. package/dist/esm/DateTimeDisplay.js.map +0 -1
  57. package/dist/esm/PeriodDisplay.d.ts +0 -6
  58. package/dist/esm/PeriodDisplay.js +0 -16
  59. package/dist/esm/PeriodDisplay.js.map +0 -1
@@ -468,11 +468,75 @@ input[type='radio']:disabled {
468
468
  background: var(--medplum-gray-100);
469
469
  }
470
470
 
471
- .medplum-resource-badge {
472
- display: inline-flex;
471
+ .modal-dialog {
472
+ background: var(--medplum-gray-600);
473
+ border: 0.1px solid var(--medplum-gray-700);
474
+ color: var(--medplum-foreground);
475
+ padding: 0;
476
+ position: absolute;
477
+ z-index: 21;
478
+ box-shadow: var(--medplum-shadow) 0 2px 6px;
479
+ outline: 0;
480
+ }
481
+
482
+ .modal-dialog a,
483
+ .modal-dialog a:link,
484
+ .modal-dialog a:visited {
485
+ color: var(--medplum-blue-800);
486
+ cursor: pointer;
487
+ }
488
+
489
+ .modal-dialog-bg {
490
+ background: rgba(0, 0, 0, 0.5);
491
+ left: 0;
492
+ position: absolute;
493
+ top: 0;
494
+ right: 0;
495
+ bottom: 0;
496
+ z-index: 20;
497
+ }
498
+
499
+ .modal-dialog-title {
500
+ background: var(--medplum-gray-800);
501
+ color: var(--medplum-surface);
502
+ cursor: pointer;
503
+ padding: 6px;
504
+ display: flex;
505
+ justify-content: space-between;
473
506
  align-items: center;
474
- gap: 4px;
475
- white-space: nowrap;
507
+ }
508
+
509
+ .modal-dialog-title-close {
510
+ cursor: pointer;
511
+ vertical-align: middle;
512
+ width: 20px;
513
+ height: 20px;
514
+ }
515
+
516
+ .modal-dialog-title-close:hover {
517
+ background-color: var(--medplum-gray-700);
518
+ }
519
+
520
+ .modal-dialog-title-close > svg {
521
+ width: 20px;
522
+ height: 20px;
523
+ }
524
+
525
+ .modal-dialog-content {
526
+ text-align: center;
527
+ background-color: var(--medplum-surface);
528
+ padding: 8px;
529
+ }
530
+
531
+ .modal-dialog-buttons {
532
+ text-align: right;
533
+ background-color: var(--medplum-gray-100);
534
+ border-top: 1px solid var(--medplum-gray-400);
535
+ padding: 8px;
536
+ }
537
+
538
+ .modal-dialog-buttons button {
539
+ padding: 1px 6px;
476
540
  }
477
541
 
478
542
  .medplum-diagnostic-report-header {
@@ -513,6 +577,13 @@ input[type='radio']:disabled {
513
577
  margin: 0;
514
578
  }
515
579
 
580
+ .medplum-resource-badge {
581
+ display: inline-flex;
582
+ align-items: center;
583
+ gap: 4px;
584
+ white-space: nowrap;
585
+ }
586
+
516
587
  table.medplum-table {
517
588
  border-collapse: collapse;
518
589
  width: 100%;
@@ -797,74 +868,6 @@ table.medplum-diff-table td {
797
868
  width: 100%;
798
869
  }
799
870
 
800
- .modal-dialog {
801
- background: var(--medplum-gray-600);
802
- border: 0.1px solid var(--medplum-gray-700);
803
- color: var(--medplum-foreground);
804
- padding: 0;
805
- position: absolute;
806
- z-index: 21;
807
- box-shadow: var(--medplum-shadow) 0 2px 6px;
808
- outline: 0;
809
- }
810
-
811
- .modal-dialog a,
812
- .modal-dialog a:link,
813
- .modal-dialog a:visited {
814
- color: var(--medplum-blue-800);
815
- cursor: pointer;
816
- }
817
-
818
- .modal-dialog-bg {
819
- background: rgba(0, 0, 0, 0.5);
820
- left: 0;
821
- position: absolute;
822
- top: 0;
823
- right: 0;
824
- bottom: 0;
825
- z-index: 20;
826
- }
827
-
828
- .modal-dialog-title {
829
- background: var(--medplum-gray-800);
830
- color: var(--medplum-surface);
831
- cursor: pointer;
832
- padding: 6px;
833
- display: flex;
834
- justify-content: space-between;
835
- }
836
-
837
- .modal-dialog-title-close {
838
- cursor: pointer;
839
- vertical-align: middle;
840
- }
841
-
842
- .modal-dialog-title-close:hover {
843
- background-color: var(--medplum-gray-700);
844
- }
845
-
846
- .modal-dialog-title-close > svg {
847
- width: 20px;
848
- height: 20px;
849
- }
850
-
851
- .modal-dialog-content {
852
- text-align: center;
853
- background-color: var(--medplum-surface);
854
- padding: 8px;
855
- }
856
-
857
- .modal-dialog-buttons {
858
- text-align: right;
859
- background-color: var(--medplum-gray-100);
860
- border-top: 1px solid var(--medplum-gray-400);
861
- padding: 8px;
862
- }
863
-
864
- .modal-dialog-buttons button {
865
- padding: 1px 6px;
866
- }
867
-
868
871
  .medplum-filter-editor {
869
872
  text-align: left;
870
873
  width: 900px;
@@ -1316,10 +1319,10 @@ div.medplum-nav-menu-container {
1316
1319
 
1317
1320
  .medplum-questionnaire-builder .section {
1318
1321
  position: relative;
1319
- margin: 4px;
1320
- padding: 16px 12px 16px 4px;
1322
+ margin: 4px 4px 4px 0;
1323
+ padding: 4px 12px 16px 2px;
1321
1324
  background-color: var(--medplum-surface);
1322
- border: 1px solid var(--medplum-gray-300);
1325
+ border: 1px solid var(--medplum-surface);
1323
1326
  border-radius: 8px;
1324
1327
  }
1325
1328
 
@@ -1328,18 +1331,19 @@ div.medplum-nav-menu-container {
1328
1331
  }
1329
1332
 
1330
1333
  .medplum-questionnaire-builder .section.hovering {
1331
- border-color: var(--medplum-blue-500);
1334
+ border: 1px solid var(--medplum-blue-500);
1332
1335
  }
1333
1336
 
1334
1337
  .medplum-questionnaire-builder .section.editing {
1335
- margin: 8px 4px 12px 4px;
1336
- padding: 4px 4px 20px 4px;
1338
+ margin: 8px 4px 12px 0;
1339
+ padding: 4px 4px 20px 12px;
1340
+ border: 1px solid var(--medplum-gray-300);
1337
1341
  border-left: 4px solid var(--medplum-blue-500);
1338
1342
  }
1339
1343
 
1340
1344
  .medplum-questionnaire-builder .section.editing input,
1341
1345
  .medplum-questionnaire-builder .section.editing select {
1342
- margin: 2px 4px;
1346
+ margin: 2px 4px 2px 0;
1343
1347
  }
1344
1348
 
1345
1349
  .medplum-questionnaire-builder .section > .top-actions {
@@ -7,6 +7,7 @@ export interface AutocompleteProps<T> {
7
7
  defaultValue?: T[];
8
8
  className?: string;
9
9
  placeholder?: string;
10
+ loadOnFocus?: boolean;
10
11
  loadOptions: (input: string, signal: AbortSignal) => Promise<T[]>;
11
12
  buildUnstructured?: (input: string) => T;
12
13
  getId: (item: T) => string;
@@ -5,7 +5,7 @@ function Autocomplete(props) {
5
5
  var _a, _b;
6
6
  const inputRef = useRef(null);
7
7
  const [focused, setFocused] = useState(false);
8
- const [lastValue, setLastValue] = useState('');
8
+ const [lastValue, setLastValue] = useState(undefined);
9
9
  const [timer, setTimer] = useState();
10
10
  const [dropDownVisible, setDropDownVisible] = useState(false);
11
11
  const [values, setValues] = useState((_a = props.defaultValue) !== null && _a !== void 0 ? _a : []);
@@ -59,6 +59,9 @@ function Autocomplete(props) {
59
59
  }
60
60
  function handleFocus() {
61
61
  setFocused(true);
62
+ if (props.loadOnFocus) {
63
+ handleInput();
64
+ }
62
65
  }
63
66
  function handleBlur() {
64
67
  setFocused(false);
@@ -198,13 +201,6 @@ function Autocomplete(props) {
198
201
  // Nothing has changed, move on
199
202
  return;
200
203
  }
201
- if (!value) {
202
- setDropDownVisible(false);
203
- setLastValue('');
204
- setOptions([]);
205
- setSelectedIndex(-1);
206
- return;
207
- }
208
204
  setLastValue(value);
209
205
  const newAbortController = new AbortController();
210
206
  setAbortController(newAbortController);
@@ -1 +1 @@
1
- {"version":3,"file":"Autocomplete.js","sources":["../../src/Autocomplete.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport { killEvent } from './utils/dom';\nimport './Autocomplete.css';\n\nexport interface AutocompleteProps<T> {\n name: string;\n multiple?: boolean;\n autofocus?: boolean;\n defaultValue?: T[];\n className?: string;\n placeholder?: string;\n loadOptions: (input: string, signal: AbortSignal) => Promise<T[]>;\n buildUnstructured?: (input: string) => T;\n getId: (item: T) => string;\n getIcon?: (item: T) => JSX.Element;\n getDisplay: (item: T) => JSX.Element;\n getHelpText?: (item: T) => string | undefined;\n onChange?: (values: T[]) => void;\n onCreateNew?: () => void;\n}\n\nexport function Autocomplete<T>(props: AutocompleteProps<T>): JSX.Element {\n const inputRef = useRef<HTMLInputElement>(null);\n const [focused, setFocused] = useState(false);\n const [lastValue, setLastValue] = useState('');\n const [timer, setTimer] = useState<number>();\n const [dropDownVisible, setDropDownVisible] = useState(false);\n const [values, setValues] = useState(props.defaultValue ?? []);\n const [options, setOptions] = useState<T[]>([]);\n const [selectedIndex, setSelectedIndex] = useState(-1);\n const [abortController, setAbortController] = useState<AbortController>();\n const [autoSubmit, setAutoSubmit] = useState<boolean>();\n\n const lastValueRef = useRef<string>();\n lastValueRef.current = lastValue;\n\n const timerRef = useRef<number>();\n timerRef.current = timer;\n\n const abortControllerRef = useRef<AbortController>();\n abortControllerRef.current = abortController;\n\n const autoSubmitRef = useRef<boolean>();\n autoSubmitRef.current = autoSubmit;\n\n useEffect(() => {\n return () => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n };\n }, []);\n\n useEffect(() => {\n setValues(props.defaultValue ?? []);\n }, [props.defaultValue]);\n\n function setValuesWrapper(newValues: T[]): void {\n setValues(newValues);\n if (props.onChange) {\n props.onChange(newValues);\n }\n }\n\n /**\n * Adds an option to the list of selected options.\n * @param option The option.\n */\n function addOption(option: T): void {\n const inputElement = inputRef.current as HTMLInputElement;\n inputElement.value = '';\n\n const newValues = props.multiple ? [...values, option] : [option];\n setFocused(true);\n setDropDownVisible(false);\n setLastValue('');\n setOptions([]);\n setSelectedIndex(-1);\n setValuesWrapper(newValues);\n }\n\n function handleClick(): void {\n inputRef.current?.focus();\n }\n\n function handleFocus(): void {\n setFocused(true);\n }\n\n function handleBlur(): void {\n setFocused(false);\n dismissOnDelay();\n }\n\n /**\n * Handles an input event.\n * Clears existing timers.\n * Schedules a timer to execute the search after a short delay.\n */\n function handleInput(): void {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n setAbortController(undefined);\n }\n\n if (timerRef.current !== undefined) {\n window.clearTimeout(timerRef.current);\n }\n\n const newTimer = window.setTimeout(() => handleTimer(), 100);\n setTimer(newTimer);\n }\n\n function handleKeyDown(e: React.KeyboardEvent): void {\n switch (e.key) {\n case 'Enter':\n case 'Tab':\n handleSelectKey(e);\n break;\n\n case 'ArrowUp':\n moveSelection(-1);\n killEvent(e);\n break;\n\n case 'ArrowDown':\n moveSelection(1);\n killEvent(e);\n break;\n\n case 'Backspace':\n handleBackspaceKey(e);\n break;\n\n case ',':\n case ';':\n handleSeparatorKey(e);\n }\n }\n\n /**\n * Handles the \"enter\" or \"tab\" keys. The enter key logic is:\n * Try to add an option with tryAddOption. On success, cancel event.\n * Otherwise, let the browser handle the enter key normally.\n *\n * @param e The key down event.\n */\n function handleSelectKey(e: React.KeyboardEvent): void {\n if (!timerRef.current && !abortControllerRef.current && tryAddOption()) {\n killEvent(e);\n inputRef.current?.focus();\n } else {\n // The user pressed enter, but we don't have results yet.\n // We need to wait for the results to come in.\n setAutoSubmit(true);\n }\n }\n\n /**\n * Handles the \"backspace\" key. The backspace key logic is:\n * If the input is empty and there is at least one item, delete the last item.\n * Otherwise, let the browser handle the backspace key normally.\n *\n * @param e The key down event.\n */\n function handleBackspaceKey(e: React.KeyboardEvent): void {\n if (inputRef.current?.value) {\n // If there is still text in the input,\n // then handle backspace as normal.\n return;\n }\n\n if (values.length > 0) {\n // If there are selected items,\n // then delete the last item.\n killEvent(e);\n setValuesWrapper(values.slice(0, values.length - 1));\n }\n }\n\n /**\n * Handles a \"separator\" key (comma, semicolon, others?).\n *\n * The separator key logic is:\n * If the drop down is visible and something is selected, choose that.\n * If the drop down is visible but nothing is selected, choose the first.\n * If there is content in the input, use that as a \"gray\" option.\n * Otherwise, ignore.\n *\n * @param e The key down event.\n */\n function handleSeparatorKey(e: React.KeyboardEvent): void {\n tryAddOption();\n killEvent(e);\n inputRef.current?.focus();\n }\n\n /**\n * Tries to capture the existing input as an option.\n *\n * @return True if captured an option; false otherwise.\n */\n function tryAddOption(): boolean {\n let option: T | undefined;\n\n if (selectedIndex >= 0 && selectedIndex < options.length) {\n // Currently highlighted row\n option = options[selectedIndex];\n } else if (selectedIndex === -1 && options.length > 0) {\n // Default to first row\n option = options[0];\n } else if (props.buildUnstructured && inputRef.current?.value) {\n // Build semi-structured item\n option = props.buildUnstructured(inputRef.current.value);\n }\n\n if (!option) {\n return false;\n }\n\n addOption(option);\n return true;\n }\n\n /**\n * Handles a timer tick event.\n * If the contents of the input have changed, sends xhr to the server\n * for updated contents.\n */\n function handleTimer(): void {\n setTimer(undefined);\n\n const value = inputRef.current?.value?.trim() || '';\n if (value === lastValueRef.current) {\n // Nothing has changed, move on\n return;\n }\n\n if (!value) {\n setDropDownVisible(false);\n setLastValue('');\n setOptions([]);\n setSelectedIndex(-1);\n return;\n }\n\n setLastValue(value);\n\n const newAbortController = new AbortController();\n setAbortController(newAbortController);\n\n props\n .loadOptions(value, newAbortController.signal)\n .then((newOptions: T[]) => {\n if (!newAbortController.signal.aborted) {\n setDropDownVisible(newOptions.length > 0);\n setOptions(newOptions);\n setAbortController(undefined);\n if (autoSubmitRef.current) {\n addOption(newOptions[0]);\n setAutoSubmit(false);\n }\n }\n })\n .catch(console.log);\n }\n\n /**\n * Moves the selected row.\n * @param delta The amount to move the selection, up is negative.\n */\n function moveSelection(delta: number): void {\n let index = selectedIndex + delta;\n\n if (index < 0) {\n index = 0;\n } else if (index >= options.length) {\n index = options.length - 1;\n }\n\n setSelectedIndex(index);\n }\n\n /**\n * Handles a hover event.\n * @param _e The mouse event.\n * @param index The drop down option index.\n */\n function handleDropDownHover(_e: React.MouseEvent, index: number): void {\n setSelectedIndex(index);\n }\n\n /**\n * Handles a click event.\n * @param e The mouse event.\n * @param option The drop down option.\n */\n function handleDropDownClick(e: React.MouseEvent, option: T): void {\n killEvent(e);\n addOption(option);\n }\n\n /**\n * Dismisses the drop down menu after a slight delay.\n */\n function dismissOnDelay(): void {\n window.setTimeout(() => {\n setDropDownVisible(false);\n }, 200);\n }\n\n const baseClassName = props.className ?? 'medplum-autocomplete-container';\n\n return (\n <div\n data-testid=\"autocomplete\"\n className={baseClassName + (focused ? ' focused' : '')}\n onClick={() => handleClick()}\n >\n <ul onClick={() => handleClick()}>\n {values.map((value) => (\n <li key={props.getId(value)} data-testid=\"selected\" className=\"medplum-autocomplete-item choice\">\n {props.getDisplay(value)}\n </li>\n ))}\n <li className=\"medplum-autocomplete-item\">\n <input\n type=\"text\"\n autoFocus={props.autofocus}\n placeholder={values.length === 0 ? props.placeholder : undefined}\n autoComplete=\"off\"\n autoCapitalize=\"off\"\n spellCheck=\"true\"\n onFocus={() => handleFocus()}\n onBlur={() => handleBlur()}\n onChange={() => handleInput()}\n onInput={() => handleInput()}\n onKeyDown={(e: React.KeyboardEvent) => handleKeyDown(e)}\n ref={inputRef}\n data-testid=\"input-element\"\n />\n </li>\n </ul>\n {dropDownVisible && (\n <div className=\"medplum-autocomplete\" data-testid=\"dropdown\">\n {options.map((option, index) => (\n <div\n key={props.getId(option)}\n className={\n index === selectedIndex\n ? 'medplum-autocomplete-row medplum-autocomplete-active'\n : 'medplum-autocomplete-row'\n }\n onMouseOver={(e) => handleDropDownHover(e, index)}\n onClick={(e) => handleDropDownClick(e, option)}\n >\n {props.getIcon && <div className=\"medplum-autocomplete-icon\">{props.getIcon(option)}</div>}\n <div className=\"medplum-autocomplete-label\">\n {props.getDisplay(option)}\n {props.getHelpText && <div className=\"medplum-autocomplete-help-text\">{props.getHelpText(option)}</div>}\n </div>\n </div>\n ))}\n {props.onCreateNew && (\n <div className=\"medplum-autocomplete-row\" onClick={props.onCreateNew}>\n <div className=\"medplum-autocomplete-label\">Create new...</div>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Autocomplete.js","sources":["../../src/Autocomplete.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react';\nimport './Autocomplete.css';\nimport { killEvent } from './utils/dom';\n\nexport interface AutocompleteProps<T> {\n name: string;\n multiple?: boolean;\n autofocus?: boolean;\n defaultValue?: T[];\n className?: string;\n placeholder?: string;\n loadOnFocus?: boolean;\n loadOptions: (input: string, signal: AbortSignal) => Promise<T[]>;\n buildUnstructured?: (input: string) => T;\n getId: (item: T) => string;\n getIcon?: (item: T) => JSX.Element;\n getDisplay: (item: T) => JSX.Element;\n getHelpText?: (item: T) => string | undefined;\n onChange?: (values: T[]) => void;\n onCreateNew?: () => void;\n}\n\nexport function Autocomplete<T>(props: AutocompleteProps<T>): JSX.Element {\n const inputRef = useRef<HTMLInputElement>(null);\n const [focused, setFocused] = useState(false);\n const [lastValue, setLastValue] = useState<string | undefined>(undefined);\n const [timer, setTimer] = useState<number>();\n const [dropDownVisible, setDropDownVisible] = useState(false);\n const [values, setValues] = useState(props.defaultValue ?? []);\n const [options, setOptions] = useState<T[]>([]);\n const [selectedIndex, setSelectedIndex] = useState(-1);\n const [abortController, setAbortController] = useState<AbortController>();\n const [autoSubmit, setAutoSubmit] = useState<boolean>();\n\n const lastValueRef = useRef<string>();\n lastValueRef.current = lastValue;\n\n const timerRef = useRef<number>();\n timerRef.current = timer;\n\n const abortControllerRef = useRef<AbortController>();\n abortControllerRef.current = abortController;\n\n const autoSubmitRef = useRef<boolean>();\n autoSubmitRef.current = autoSubmit;\n\n useEffect(() => {\n return () => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n };\n }, []);\n\n useEffect(() => {\n setValues(props.defaultValue ?? []);\n }, [props.defaultValue]);\n\n function setValuesWrapper(newValues: T[]): void {\n setValues(newValues);\n if (props.onChange) {\n props.onChange(newValues);\n }\n }\n\n /**\n * Adds an option to the list of selected options.\n * @param option The option.\n */\n function addOption(option: T): void {\n const inputElement = inputRef.current as HTMLInputElement;\n inputElement.value = '';\n\n const newValues = props.multiple ? [...values, option] : [option];\n setFocused(true);\n setDropDownVisible(false);\n setLastValue('');\n setOptions([]);\n setSelectedIndex(-1);\n setValuesWrapper(newValues);\n }\n\n function handleClick(): void {\n inputRef.current?.focus();\n }\n\n function handleFocus(): void {\n setFocused(true);\n if (props.loadOnFocus) {\n handleInput();\n }\n }\n\n function handleBlur(): void {\n setFocused(false);\n dismissOnDelay();\n }\n\n /**\n * Handles an input event.\n * Clears existing timers.\n * Schedules a timer to execute the search after a short delay.\n */\n function handleInput(): void {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n setAbortController(undefined);\n }\n\n if (timerRef.current !== undefined) {\n window.clearTimeout(timerRef.current);\n }\n\n const newTimer = window.setTimeout(() => handleTimer(), 100);\n setTimer(newTimer);\n }\n\n function handleKeyDown(e: React.KeyboardEvent): void {\n switch (e.key) {\n case 'Enter':\n case 'Tab':\n handleSelectKey(e);\n break;\n\n case 'ArrowUp':\n moveSelection(-1);\n killEvent(e);\n break;\n\n case 'ArrowDown':\n moveSelection(1);\n killEvent(e);\n break;\n\n case 'Backspace':\n handleBackspaceKey(e);\n break;\n\n case ',':\n case ';':\n handleSeparatorKey(e);\n }\n }\n\n /**\n * Handles the \"enter\" or \"tab\" keys. The enter key logic is:\n * Try to add an option with tryAddOption. On success, cancel event.\n * Otherwise, let the browser handle the enter key normally.\n *\n * @param e The key down event.\n */\n function handleSelectKey(e: React.KeyboardEvent): void {\n if (!timerRef.current && !abortControllerRef.current && tryAddOption()) {\n killEvent(e);\n inputRef.current?.focus();\n } else {\n // The user pressed enter, but we don't have results yet.\n // We need to wait for the results to come in.\n setAutoSubmit(true);\n }\n }\n\n /**\n * Handles the \"backspace\" key. The backspace key logic is:\n * If the input is empty and there is at least one item, delete the last item.\n * Otherwise, let the browser handle the backspace key normally.\n *\n * @param e The key down event.\n */\n function handleBackspaceKey(e: React.KeyboardEvent): void {\n if (inputRef.current?.value) {\n // If there is still text in the input,\n // then handle backspace as normal.\n return;\n }\n\n if (values.length > 0) {\n // If there are selected items,\n // then delete the last item.\n killEvent(e);\n setValuesWrapper(values.slice(0, values.length - 1));\n }\n }\n\n /**\n * Handles a \"separator\" key (comma, semicolon, others?).\n *\n * The separator key logic is:\n * If the drop down is visible and something is selected, choose that.\n * If the drop down is visible but nothing is selected, choose the first.\n * If there is content in the input, use that as a \"gray\" option.\n * Otherwise, ignore.\n *\n * @param e The key down event.\n */\n function handleSeparatorKey(e: React.KeyboardEvent): void {\n tryAddOption();\n killEvent(e);\n inputRef.current?.focus();\n }\n\n /**\n * Tries to capture the existing input as an option.\n *\n * @return True if captured an option; false otherwise.\n */\n function tryAddOption(): boolean {\n let option: T | undefined;\n\n if (selectedIndex >= 0 && selectedIndex < options.length) {\n // Currently highlighted row\n option = options[selectedIndex];\n } else if (selectedIndex === -1 && options.length > 0) {\n // Default to first row\n option = options[0];\n } else if (props.buildUnstructured && inputRef.current?.value) {\n // Build semi-structured item\n option = props.buildUnstructured(inputRef.current.value);\n }\n\n if (!option) {\n return false;\n }\n\n addOption(option);\n return true;\n }\n\n /**\n * Handles a timer tick event.\n * If the contents of the input have changed, sends xhr to the server\n * for updated contents.\n */\n function handleTimer(): void {\n setTimer(undefined);\n\n const value = inputRef.current?.value?.trim() || '';\n if (value === lastValueRef.current) {\n // Nothing has changed, move on\n return;\n }\n\n setLastValue(value);\n\n const newAbortController = new AbortController();\n setAbortController(newAbortController);\n\n props\n .loadOptions(value, newAbortController.signal)\n .then((newOptions: T[]) => {\n if (!newAbortController.signal.aborted) {\n setDropDownVisible(newOptions.length > 0);\n setOptions(newOptions);\n setAbortController(undefined);\n if (autoSubmitRef.current) {\n addOption(newOptions[0]);\n setAutoSubmit(false);\n }\n }\n })\n .catch(console.log);\n }\n\n /**\n * Moves the selected row.\n * @param delta The amount to move the selection, up is negative.\n */\n function moveSelection(delta: number): void {\n let index = selectedIndex + delta;\n\n if (index < 0) {\n index = 0;\n } else if (index >= options.length) {\n index = options.length - 1;\n }\n\n setSelectedIndex(index);\n }\n\n /**\n * Handles a hover event.\n * @param _e The mouse event.\n * @param index The drop down option index.\n */\n function handleDropDownHover(_e: React.MouseEvent, index: number): void {\n setSelectedIndex(index);\n }\n\n /**\n * Handles a click event.\n * @param e The mouse event.\n * @param option The drop down option.\n */\n function handleDropDownClick(e: React.MouseEvent, option: T): void {\n killEvent(e);\n addOption(option);\n }\n\n /**\n * Dismisses the drop down menu after a slight delay.\n */\n function dismissOnDelay(): void {\n window.setTimeout(() => {\n setDropDownVisible(false);\n }, 200);\n }\n\n const baseClassName = props.className ?? 'medplum-autocomplete-container';\n\n return (\n <div\n data-testid=\"autocomplete\"\n className={baseClassName + (focused ? ' focused' : '')}\n onClick={() => handleClick()}\n >\n <ul onClick={() => handleClick()}>\n {values.map((value) => (\n <li key={props.getId(value)} data-testid=\"selected\" className=\"medplum-autocomplete-item choice\">\n {props.getDisplay(value)}\n </li>\n ))}\n <li className=\"medplum-autocomplete-item\">\n <input\n type=\"text\"\n autoFocus={props.autofocus}\n placeholder={values.length === 0 ? props.placeholder : undefined}\n autoComplete=\"off\"\n autoCapitalize=\"off\"\n spellCheck=\"true\"\n onFocus={() => handleFocus()}\n onBlur={() => handleBlur()}\n onChange={() => handleInput()}\n onInput={() => handleInput()}\n onKeyDown={(e: React.KeyboardEvent) => handleKeyDown(e)}\n ref={inputRef}\n data-testid=\"input-element\"\n />\n </li>\n </ul>\n {dropDownVisible && (\n <div className=\"medplum-autocomplete\" data-testid=\"dropdown\">\n {options.map((option, index) => (\n <div\n key={props.getId(option)}\n className={\n index === selectedIndex\n ? 'medplum-autocomplete-row medplum-autocomplete-active'\n : 'medplum-autocomplete-row'\n }\n onMouseOver={(e) => handleDropDownHover(e, index)}\n onClick={(e) => handleDropDownClick(e, option)}\n >\n {props.getIcon && <div className=\"medplum-autocomplete-icon\">{props.getIcon(option)}</div>}\n <div className=\"medplum-autocomplete-label\">\n {props.getDisplay(option)}\n {props.getHelpText && <div className=\"medplum-autocomplete-help-text\">{props.getHelpText(option)}</div>}\n </div>\n </div>\n ))}\n {props.onCreateNew && (\n <div className=\"medplum-autocomplete-row\" onClick={props.onCreateNew}>\n <div className=\"medplum-autocomplete-label\">Create new...</div>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -9,8 +9,10 @@ function CodeableConceptDisplay(props) {
9
9
  if (value.text) {
10
10
  return React.createElement(React.Fragment, null, value.text);
11
11
  }
12
- if (value.coding && value.coding.length > 0) {
13
- return React.createElement(CodingDisplay, { value: value.coding[0] });
12
+ if (value.coding) {
13
+ return (React.createElement(React.Fragment, null, value.coding.map((coding, index) => (React.createElement(React.Fragment, { key: 'coding-' + index },
14
+ index > 0 && React.createElement(React.Fragment, null, ', '),
15
+ React.createElement(CodingDisplay, { value: coding }))))));
14
16
  }
15
17
  return null;
16
18
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CodeableConceptDisplay.js","sources":["../../src/CodeableConceptDisplay.tsx"],"sourcesContent":["import { CodeableConcept } from '@medplum/fhirtypes';\nimport React from 'react';\nimport { CodingDisplay } from './CodingDisplay';\n\nexport interface CodeableConceptDisplayProps {\n value?: CodeableConcept;\n}\n\nexport function CodeableConceptDisplay(props: CodeableConceptDisplayProps): JSX.Element | null {\n const value = props.value;\n if (!value) {\n return null;\n }\n\n if (value.text) {\n return <>{value.text}</>;\n }\n\n if (value.coding && value.coding.length > 0) {\n return <CodingDisplay value={value.coding[0]} />;\n }\n\n return null;\n}\n"],"names":[],"mappings":";;;AAQM,SAAU,sBAAsB,CAAC,KAAkC,EAAA;AACvE,IAAA,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IAC1B,IAAI,CAAC,KAAK,EAAE;AACV,QAAA,OAAO,IAAI,CAAC;AACb,KAAA;IAED,IAAI,KAAK,CAAC,IAAI,EAAE;AACd,QAAA,OAAO,KAAG,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,EAAA,KAAK,CAAC,IAAI,CAAI,CAAC;AAC1B,KAAA;IAED,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;AAC3C,QAAA,OAAO,KAAC,CAAA,aAAA,CAAA,aAAa,EAAC,EAAA,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAA,CAAI,CAAC;AAClD,KAAA;AAED,IAAA,OAAO,IAAI,CAAC;AACd;;;;"}
1
+ {"version":3,"file":"CodeableConceptDisplay.js","sources":["../../src/CodeableConceptDisplay.tsx"],"sourcesContent":["import { CodeableConcept } from '@medplum/fhirtypes';\nimport React from 'react';\nimport { CodingDisplay } from './CodingDisplay';\n\nexport interface CodeableConceptDisplayProps {\n value?: CodeableConcept;\n}\n\nexport function CodeableConceptDisplay(props: CodeableConceptDisplayProps): JSX.Element | null {\n const value = props.value;\n if (!value) {\n return null;\n }\n\n if (value.text) {\n return <>{value.text}</>;\n }\n\n if (value.coding) {\n return (\n <>\n {value.coding.map((coding, index) => (\n <React.Fragment key={'coding-' + index}>\n {index > 0 && <>{', '}</>}\n <CodingDisplay value={coding} />\n </React.Fragment>\n ))}\n </>\n );\n }\n\n return null;\n}\n"],"names":[],"mappings":";;;AAQM,SAAU,sBAAsB,CAAC,KAAkC,EAAA;AACvE,IAAA,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;IAC1B,IAAI,CAAC,KAAK,EAAE;AACV,QAAA,OAAO,IAAI,CAAC;AACb,KAAA;IAED,IAAI,KAAK,CAAC,IAAI,EAAE;AACd,QAAA,OAAO,KAAG,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,EAAA,KAAK,CAAC,IAAI,CAAI,CAAC;AAC1B,KAAA;IAED,IAAI,KAAK,CAAC,MAAM,EAAE;QAChB,QACE,KACG,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,EAAA,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,MAC9B,KAAA,CAAA,aAAA,CAAC,KAAK,CAAC,QAAQ,EAAA,EAAC,GAAG,EAAE,SAAS,GAAG,KAAK,EAAA;AACnC,YAAA,KAAK,GAAG,CAAC,IAAI,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,EAAG,IAAI,CAAI;YACzB,KAAC,CAAA,aAAA,CAAA,aAAa,EAAC,EAAA,KAAK,EAAE,MAAM,EAAI,CAAA,CACjB,CAClB,CAAC,CACD,EACH;AACH,KAAA;AAED,IAAA,OAAO,IAAI,CAAC;AACd;;;;"}
@@ -1,3 +1,4 @@
1
+ import { isValidDate } from '@medplum/core';
1
2
  import React from 'react';
2
3
  import { Input } from './Input.js';
3
4
 
@@ -54,9 +55,6 @@ function convertLocalToIso(localString) {
54
55
  }
55
56
  return date.toISOString();
56
57
  }
57
- function isValidDate(date) {
58
- return date instanceof Date && !isNaN(date.getTime());
59
- }
60
58
 
61
59
  export { DateTimeInput, convertIsoToLocal, convertLocalToIso };
62
60
  //# sourceMappingURL=DateTimeInput.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DateTimeInput.js","sources":["../../src/DateTimeInput.tsx"],"sourcesContent":["import React from 'react';\nimport { Input, InputProps } from './Input';\n\n/**\n * The DateTimeInput component is a wrapper around the HTML5 input type=\"datetime-local\".\n * The main purpose is to reconcile time zones.\n * Most of our date/time values are in ISO-8601, which includes a time zone offset.\n * The datetime-local input does not support the time zone offset.\n * @param props The Input props.\n * @returns The JSX element to render.\n */\nexport function DateTimeInput(props: InputProps): JSX.Element {\n return (\n <Input\n {...props}\n type=\"datetime-local\"\n defaultValue={convertIsoToLocal(props.defaultValue as string | undefined)}\n onChange={(newValue: string) => {\n if (props.onChange) {\n props.onChange(convertLocalToIso(newValue));\n }\n }}\n />\n );\n}\n\n/**\n * Converts an ISO-8601 date/time string to a local date/time string.\n * @param isoString The ISO-8601 date/time string to convert.\n * @returns The local date/time string.\n */\nexport function convertIsoToLocal(isoString: string | undefined): string {\n if (!isoString) {\n return '';\n }\n\n // Convert the ISO-8601 date to a local datetime-local value.\n // See: https://developer.mozilla.org/en-US/docs/Web/HTML/Date_and_time_formats#local_date_and_time_strings\n // See: https://stackoverflow.com/a/60368477\n const date = new Date(isoString);\n if (!isValidDate(date)) {\n // If the input string was malformed, return an empty string.\n return '';\n }\n\n return date.toLocaleDateString('sv') + 'T' + date.toLocaleTimeString('sv');\n}\n\n/**\n * Converts a local date/time string to an ISO-8601 date/time string.\n * @param localString The local date/time string to convert.\n * @returns The ISO-8601 date/time string.\n */\nexport function convertLocalToIso(localString: string | undefined): string {\n if (!localString) {\n return '';\n }\n\n // Try to parse the local string as a Date\n // JavaScript's Date() constructor defaults to the local time zone.\n // The Date() constructor will throw if the value is malformed.\n const date = new Date(localString);\n if (!isValidDate(date)) {\n // If the input string was malformed, return an empty string.\n return '';\n }\n\n return date.toISOString();\n}\n\nfunction isValidDate(date: Date): boolean {\n return date instanceof Date && !isNaN(date.getTime());\n}\n"],"names":[],"mappings":";;;AAGA;;;;;;;AAOG;AACG,SAAU,aAAa,CAAC,KAAiB,EAAA;IAC7C,QACE,KAAC,CAAA,aAAA,CAAA,KAAK,EACA,MAAA,CAAA,MAAA,CAAA,EAAA,EAAA,KAAK,IACT,IAAI,EAAC,gBAAgB,EACrB,YAAY,EAAE,iBAAiB,CAAC,KAAK,CAAC,YAAkC,CAAC,EACzE,QAAQ,EAAE,CAAC,QAAgB,KAAI;YAC7B,IAAI,KAAK,CAAC,QAAQ,EAAE;gBAClB,KAAK,CAAC,QAAQ,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC7C,aAAA;SACF,EAAA,CAAA,CACD,EACF;AACJ,CAAC;AAED;;;;AAIG;AACG,SAAU,iBAAiB,CAAC,SAA6B,EAAA;IAC7D,IAAI,CAAC,SAAS,EAAE;AACd,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;;;;AAKD,IAAA,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;AACjC,IAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;;AAEtB,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;AAED,IAAA,OAAO,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;AAC7E,CAAC;AAED;;;;AAIG;AACG,SAAU,iBAAiB,CAAC,WAA+B,EAAA;IAC/D,IAAI,CAAC,WAAW,EAAE;AAChB,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;;;;AAKD,IAAA,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC;AACnC,IAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;;AAEtB,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;AAED,IAAA,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC5B,CAAC;AAED,SAAS,WAAW,CAAC,IAAU,EAAA;AAC7B,IAAA,OAAO,IAAI,YAAY,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;AACxD;;;;"}
1
+ {"version":3,"file":"DateTimeInput.js","sources":["../../src/DateTimeInput.tsx"],"sourcesContent":["import { isValidDate } from '@medplum/core';\nimport React from 'react';\nimport { Input, InputProps } from './Input';\n\n/**\n * The DateTimeInput component is a wrapper around the HTML5 input type=\"datetime-local\".\n * The main purpose is to reconcile time zones.\n * Most of our date/time values are in ISO-8601, which includes a time zone offset.\n * The datetime-local input does not support the time zone offset.\n * @param props The Input props.\n * @returns The JSX element to render.\n */\nexport function DateTimeInput(props: InputProps): JSX.Element {\n return (\n <Input\n {...props}\n type=\"datetime-local\"\n defaultValue={convertIsoToLocal(props.defaultValue as string | undefined)}\n onChange={(newValue: string) => {\n if (props.onChange) {\n props.onChange(convertLocalToIso(newValue));\n }\n }}\n />\n );\n}\n\n/**\n * Converts an ISO-8601 date/time string to a local date/time string.\n * @param isoString The ISO-8601 date/time string to convert.\n * @returns The local date/time string.\n */\nexport function convertIsoToLocal(isoString: string | undefined): string {\n if (!isoString) {\n return '';\n }\n\n // Convert the ISO-8601 date to a local datetime-local value.\n // See: https://developer.mozilla.org/en-US/docs/Web/HTML/Date_and_time_formats#local_date_and_time_strings\n // See: https://stackoverflow.com/a/60368477\n const date = new Date(isoString);\n if (!isValidDate(date)) {\n // If the input string was malformed, return an empty string.\n return '';\n }\n\n return date.toLocaleDateString('sv') + 'T' + date.toLocaleTimeString('sv');\n}\n\n/**\n * Converts a local date/time string to an ISO-8601 date/time string.\n * @param localString The local date/time string to convert.\n * @returns The ISO-8601 date/time string.\n */\nexport function convertLocalToIso(localString: string | undefined): string {\n if (!localString) {\n return '';\n }\n\n // Try to parse the local string as a Date\n // JavaScript's Date() constructor defaults to the local time zone.\n // The Date() constructor will throw if the value is malformed.\n const date = new Date(localString);\n if (!isValidDate(date)) {\n // If the input string was malformed, return an empty string.\n return '';\n }\n\n return date.toISOString();\n}\n"],"names":[],"mappings":";;;;AAIA;;;;;;;AAOG;AACG,SAAU,aAAa,CAAC,KAAiB,EAAA;IAC7C,QACE,KAAC,CAAA,aAAA,CAAA,KAAK,EACA,MAAA,CAAA,MAAA,CAAA,EAAA,EAAA,KAAK,IACT,IAAI,EAAC,gBAAgB,EACrB,YAAY,EAAE,iBAAiB,CAAC,KAAK,CAAC,YAAkC,CAAC,EACzE,QAAQ,EAAE,CAAC,QAAgB,KAAI;YAC7B,IAAI,KAAK,CAAC,QAAQ,EAAE;gBAClB,KAAK,CAAC,QAAQ,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC7C,aAAA;SACF,EAAA,CAAA,CACD,EACF;AACJ,CAAC;AAED;;;;AAIG;AACG,SAAU,iBAAiB,CAAC,SAA6B,EAAA;IAC7D,IAAI,CAAC,SAAS,EAAE;AACd,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;;;;AAKD,IAAA,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC;AACjC,IAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;;AAEtB,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;AAED,IAAA,OAAO,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;AAC7E,CAAC;AAED;;;;AAIG;AACG,SAAU,iBAAiB,CAAC,WAA+B,EAAA;IAC/D,IAAI,CAAC,WAAW,EAAE;AAChB,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;;;;AAKD,IAAA,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC;AACnC,IAAA,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE;;AAEtB,QAAA,OAAO,EAAE,CAAC;AACX,KAAA;AAED,IAAA,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;AAC5B;;;;"}
@@ -1,7 +1,6 @@
1
- import { capitalize } from '@medplum/core';
1
+ import { formatDateTime, capitalize } from '@medplum/core';
2
2
  import React from 'react';
3
3
  import { CodeableConceptDisplay } from './CodeableConceptDisplay.js';
4
- import { DateTimeDisplay } from './DateTimeDisplay.js';
5
4
  import { MedplumLink } from './MedplumLink.js';
6
5
  import { QuantityDisplay } from './QuantityDisplay.js';
7
6
  import { RangeDisplay } from './RangeDisplay.js';
@@ -41,8 +40,7 @@ function DiagnosticReportDisplay(props) {
41
40
  React.createElement(ResourceBadge, { value: interpreter, link: true }))))),
42
41
  diagnosticReport.issued && (React.createElement("dl", null,
43
42
  React.createElement("dt", null, "Issued"),
44
- React.createElement("dd", null,
45
- React.createElement(DateTimeDisplay, { value: diagnosticReport.issued })))),
43
+ React.createElement("dd", null, formatDateTime(diagnosticReport.issued)))),
46
44
  diagnosticReport.status && (React.createElement("dl", null,
47
45
  React.createElement("dt", null, "Status"),
48
46
  React.createElement("dd", null, capitalize(diagnosticReport.status))))),
@@ -1 +1 @@
1
- {"version":3,"file":"DiagnosticReportDisplay.js","sources":["../../src/DiagnosticReportDisplay.tsx"],"sourcesContent":["import { capitalize } from '@medplum/core';\nimport {\n DiagnosticReport,\n Observation,\n ObservationComponent,\n ObservationReferenceRange,\n Reference,\n} from '@medplum/fhirtypes';\nimport React from 'react';\nimport { CodeableConceptDisplay } from './CodeableConceptDisplay';\nimport { DateTimeDisplay } from './DateTimeDisplay';\nimport { MedplumLink } from './MedplumLink';\nimport { QuantityDisplay } from './QuantityDisplay';\nimport { RangeDisplay } from './RangeDisplay';\nimport { ResourceBadge } from './ResourceBadge';\nimport { useResource } from './useResource';\nimport './DiagnosticReportDisplay.css';\nimport './Table.css';\n\nexport interface DiagnosticReportDisplayProps {\n value?: DiagnosticReport | Reference<DiagnosticReport>;\n}\n\nexport function DiagnosticReportDisplay(props: DiagnosticReportDisplayProps): JSX.Element | null {\n const diagnosticReport = useResource(props.value);\n const specimen = useResource(diagnosticReport?.specimen?.[0]);\n if (!diagnosticReport) {\n return null;\n }\n\n let textContent = '';\n\n if (diagnosticReport.presentedForm && diagnosticReport.presentedForm.length > 0) {\n const pf = diagnosticReport.presentedForm[0];\n if (pf.contentType?.startsWith('text/plain') && pf.data) {\n textContent = window.atob(pf.data);\n }\n }\n\n if (specimen?.note) {\n for (const note of specimen.note) {\n textContent += note.text + '\\n\\n';\n }\n }\n\n return (\n <div className=\"medplum-diagnostic-report\">\n <h1>Diagnostic Report</h1>\n <div className=\"medplum-diagnostic-report-header\">\n {diagnosticReport.subject && (\n <dl>\n <dt>Subject</dt>\n <dd>\n <ResourceBadge value={diagnosticReport.subject} link={true} />\n </dd>\n </dl>\n )}\n {diagnosticReport.resultsInterpreter &&\n diagnosticReport.resultsInterpreter.map((interpreter) => (\n <dl key={interpreter.reference}>\n <dt>Interpreter</dt>\n <dd>\n <ResourceBadge value={interpreter} link={true} />\n </dd>\n </dl>\n ))}\n {diagnosticReport.issued && (\n <dl>\n <dt>Issued</dt>\n <dd>\n <DateTimeDisplay value={diagnosticReport.issued} />\n </dd>\n </dl>\n )}\n {diagnosticReport.status && (\n <dl>\n <dt>Status</dt>\n <dd>{capitalize(diagnosticReport.status)}</dd>\n </dl>\n )}\n </div>\n {diagnosticReport.result && <ObservationTable value={diagnosticReport.result} />}\n {textContent && <pre>{textContent.trim()}</pre>}\n </div>\n );\n}\n\nexport interface ObservationTableProps {\n value?: Observation[] | Reference<Observation>[];\n}\n\nexport function ObservationTable(props: ObservationTableProps): JSX.Element {\n return (\n <table className=\"medplum-table\">\n <thead>\n <tr>\n <th>Test</th>\n <th>Value</th>\n <th>Reference Range</th>\n <th>Interpretation</th>\n </tr>\n </thead>\n <tbody>\n {props.value?.map((observation, index) => (\n <ObservationRow key={'obs-' + index} value={observation} />\n ))}\n </tbody>\n </table>\n );\n}\n\ninterface ObservationRowProps {\n value: Observation | Reference<Observation>;\n}\n\nfunction ObservationRow(props: ObservationRowProps): JSX.Element | null {\n const observation = useResource(props.value);\n if (!observation) {\n return null;\n }\n\n return (\n <tr>\n <td>\n <MedplumLink to={observation}>\n <CodeableConceptDisplay value={observation.code} />\n </MedplumLink>\n </td>\n <td>\n <ObservationValueDisplay value={observation} />\n </td>\n <td>\n <ReferenceRangeDisplay value={observation.referenceRange} />\n </td>\n <td>\n {observation.interpretation && observation.interpretation.length > 0 && (\n <CodeableConceptDisplay value={observation.interpretation[0]} />\n )}\n </td>\n </tr>\n );\n}\n\ninterface ObservationValueDisplayProps {\n value?: Observation | ObservationComponent;\n}\n\nfunction ObservationValueDisplay(props: ObservationValueDisplayProps): JSX.Element | null {\n const obs = props.value;\n\n if (obs?.valueQuantity) {\n return <QuantityDisplay value={props.value?.valueQuantity} />;\n }\n\n if (obs?.valueString) {\n return <>{obs.valueString}</>;\n }\n\n if (obs && 'component' in obs && obs?.component) {\n return (\n <>\n {obs.component\n .map<React.ReactNode>((component: ObservationComponent, index: number) => (\n <ObservationValueDisplay key={`obs-${index}`} value={component} />\n ))\n .reduce((prev, curr) => [prev, ' / ', curr])}\n </>\n );\n }\n\n return null;\n}\n\ninterface ReferenceRangeProps {\n value?: ObservationReferenceRange[];\n}\n\nfunction ReferenceRangeDisplay(props: ReferenceRangeProps): JSX.Element | null {\n const range = props.value && props.value.length > 0 && props.value[0];\n if (!range) {\n return null;\n }\n return <RangeDisplay value={range} />;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"DiagnosticReportDisplay.js","sources":["../../src/DiagnosticReportDisplay.tsx"],"sourcesContent":["import { capitalize, formatDateTime } from '@medplum/core';\nimport {\n DiagnosticReport,\n Observation,\n ObservationComponent,\n ObservationReferenceRange,\n Reference,\n} from '@medplum/fhirtypes';\nimport React from 'react';\nimport { CodeableConceptDisplay } from './CodeableConceptDisplay';\nimport './DiagnosticReportDisplay.css';\nimport { MedplumLink } from './MedplumLink';\nimport { QuantityDisplay } from './QuantityDisplay';\nimport { RangeDisplay } from './RangeDisplay';\nimport { ResourceBadge } from './ResourceBadge';\nimport './Table.css';\nimport { useResource } from './useResource';\n\nexport interface DiagnosticReportDisplayProps {\n value?: DiagnosticReport | Reference<DiagnosticReport>;\n}\n\nexport function DiagnosticReportDisplay(props: DiagnosticReportDisplayProps): JSX.Element | null {\n const diagnosticReport = useResource(props.value);\n const specimen = useResource(diagnosticReport?.specimen?.[0]);\n if (!diagnosticReport) {\n return null;\n }\n\n let textContent = '';\n\n if (diagnosticReport.presentedForm && diagnosticReport.presentedForm.length > 0) {\n const pf = diagnosticReport.presentedForm[0];\n if (pf.contentType?.startsWith('text/plain') && pf.data) {\n textContent = window.atob(pf.data);\n }\n }\n\n if (specimen?.note) {\n for (const note of specimen.note) {\n textContent += note.text + '\\n\\n';\n }\n }\n\n return (\n <div className=\"medplum-diagnostic-report\">\n <h1>Diagnostic Report</h1>\n <div className=\"medplum-diagnostic-report-header\">\n {diagnosticReport.subject && (\n <dl>\n <dt>Subject</dt>\n <dd>\n <ResourceBadge value={diagnosticReport.subject} link={true} />\n </dd>\n </dl>\n )}\n {diagnosticReport.resultsInterpreter &&\n diagnosticReport.resultsInterpreter.map((interpreter) => (\n <dl key={interpreter.reference}>\n <dt>Interpreter</dt>\n <dd>\n <ResourceBadge value={interpreter} link={true} />\n </dd>\n </dl>\n ))}\n {diagnosticReport.issued && (\n <dl>\n <dt>Issued</dt>\n <dd>{formatDateTime(diagnosticReport.issued)}</dd>\n </dl>\n )}\n {diagnosticReport.status && (\n <dl>\n <dt>Status</dt>\n <dd>{capitalize(diagnosticReport.status)}</dd>\n </dl>\n )}\n </div>\n {diagnosticReport.result && <ObservationTable value={diagnosticReport.result} />}\n {textContent && <pre>{textContent.trim()}</pre>}\n </div>\n );\n}\n\nexport interface ObservationTableProps {\n value?: Observation[] | Reference<Observation>[];\n}\n\nexport function ObservationTable(props: ObservationTableProps): JSX.Element {\n return (\n <table className=\"medplum-table\">\n <thead>\n <tr>\n <th>Test</th>\n <th>Value</th>\n <th>Reference Range</th>\n <th>Interpretation</th>\n </tr>\n </thead>\n <tbody>\n {props.value?.map((observation, index) => (\n <ObservationRow key={'obs-' + index} value={observation} />\n ))}\n </tbody>\n </table>\n );\n}\n\ninterface ObservationRowProps {\n value: Observation | Reference<Observation>;\n}\n\nfunction ObservationRow(props: ObservationRowProps): JSX.Element | null {\n const observation = useResource(props.value);\n if (!observation) {\n return null;\n }\n\n return (\n <tr>\n <td>\n <MedplumLink to={observation}>\n <CodeableConceptDisplay value={observation.code} />\n </MedplumLink>\n </td>\n <td>\n <ObservationValueDisplay value={observation} />\n </td>\n <td>\n <ReferenceRangeDisplay value={observation.referenceRange} />\n </td>\n <td>\n {observation.interpretation && observation.interpretation.length > 0 && (\n <CodeableConceptDisplay value={observation.interpretation[0]} />\n )}\n </td>\n </tr>\n );\n}\n\ninterface ObservationValueDisplayProps {\n value?: Observation | ObservationComponent;\n}\n\nfunction ObservationValueDisplay(props: ObservationValueDisplayProps): JSX.Element | null {\n const obs = props.value;\n\n if (obs?.valueQuantity) {\n return <QuantityDisplay value={props.value?.valueQuantity} />;\n }\n\n if (obs?.valueString) {\n return <>{obs.valueString}</>;\n }\n\n if (obs && 'component' in obs && obs?.component) {\n return (\n <>\n {obs.component\n .map<React.ReactNode>((component: ObservationComponent, index: number) => (\n <ObservationValueDisplay key={`obs-${index}`} value={component} />\n ))\n .reduce((prev, curr) => [prev, ' / ', curr])}\n </>\n );\n }\n\n return null;\n}\n\ninterface ReferenceRangeProps {\n value?: ObservationReferenceRange[];\n}\n\nfunction ReferenceRangeDisplay(props: ReferenceRangeProps): JSX.Element | null {\n const range = props.value && props.value.length > 0 && props.value[0];\n if (!range) {\n return null;\n }\n return <RangeDisplay value={range} />;\n}\n"],"names":[],"mappings":"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"}
@@ -37,9 +37,9 @@ function getGoogleClientId(clientId) {
37
37
  return clientId;
38
38
  }
39
39
  const origin = window.location.protocol + '//' + window.location.host;
40
- const authorizedOrigins = (_b = (_a = "undefined") === null || _a === void 0 ? void 0 : _a.split(',')) !== null && _b !== void 0 ? _b : [];
40
+ const authorizedOrigins = (_b = (_a = "http://localhost:3000,http://localhost:6006,https://app.medplum.com,https://docs.medplum.com") === null || _a === void 0 ? void 0 : _a.split(',')) !== null && _b !== void 0 ? _b : [];
41
41
  if (authorizedOrigins.includes(origin)) {
42
- return "undefined";
42
+ return "921088377005-3j1sa10vr6hj86jgmdfh2l53v3mp7lfi.apps.googleusercontent.com";
43
43
  }
44
44
  return undefined;
45
45
  }
@@ -1 +1 @@
1
- {"version":3,"file":"GoogleButton.js","sources":["../../src/GoogleButton.tsx"],"sourcesContent":["import { GoogleCredentialResponse } from '@medplum/core';\nimport React, { useEffect, useRef, useState } from 'react';\nimport { useMedplum } from './MedplumProvider';\nimport { createScriptTag } from './utils';\n\ninterface GoogleApi {\n accounts: {\n id: {\n initialize: (args: any) => void;\n renderButton: (parent: HTMLElement, args: any) => void;\n };\n };\n}\n\ndeclare const google: GoogleApi;\n\nexport interface GoogleButtonProps {\n readonly googleClientId?: string;\n readonly handleGoogleCredential: (response: GoogleCredentialResponse) => void;\n}\n\nexport function GoogleButton(props: GoogleButtonProps): JSX.Element | null {\n const medplum = useMedplum();\n const { googleClientId, handleGoogleCredential } = props;\n const parentRef = useRef<HTMLDivElement>(null);\n const [scriptLoaded, setScriptLoaded] = useState<boolean>(typeof google !== 'undefined');\n const [initialized, setInitialized] = useState<boolean>(false);\n const [buttonRendered, setButtonRendered] = useState<boolean>(false);\n\n useEffect(() => {\n if (typeof google === 'undefined') {\n createScriptTag('https://accounts.google.com/gsi/client', () => setScriptLoaded(true));\n return;\n }\n\n if (!initialized) {\n google.accounts.id.initialize({\n client_id: googleClientId,\n callback: handleGoogleCredential,\n });\n setInitialized(true);\n }\n\n if (parentRef.current && !buttonRendered) {\n google.accounts.id.renderButton(parentRef.current, {});\n setButtonRendered(true);\n }\n }, [medplum, googleClientId, initialized, scriptLoaded, parentRef, buttonRendered, handleGoogleCredential]);\n\n if (!googleClientId) {\n return null;\n }\n\n return <div ref={parentRef} />;\n}\n\nexport function getGoogleClientId(clientId: string | undefined): string | undefined {\n if (clientId) {\n return clientId;\n }\n\n const origin = window.location.protocol + '//' + window.location.host;\n const authorizedOrigins = process.env.GOOGLE_AUTH_ORIGINS?.split(',') ?? [];\n if (authorizedOrigins.includes(origin)) {\n return process.env.GOOGLE_CLIENT_ID;\n }\n\n return undefined;\n}\n"],"names":[],"mappings":";;;;AAqBM,SAAU,YAAY,CAAC,KAAwB,EAAA;AACnD,IAAA,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;AAC7B,IAAA,MAAM,EAAE,cAAc,EAAE,sBAAsB,EAAE,GAAG,KAAK,CAAC;AACzD,IAAA,MAAM,SAAS,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;AAC/C,IAAA,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAU,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC;IACzF,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC,CAAC;IAC/D,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC,CAAC;IAErE,SAAS,CAAC,MAAK;AACb,QAAA,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;YACjC,eAAe,CAAC,wCAAwC,EAAE,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;YACvF,OAAO;AACR,SAAA;QAED,IAAI,CAAC,WAAW,EAAE;AAChB,YAAA,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,UAAU,CAAC;AAC5B,gBAAA,SAAS,EAAE,cAAc;AACzB,gBAAA,QAAQ,EAAE,sBAAsB;AACjC,aAAA,CAAC,CAAC;YACH,cAAc,CAAC,IAAI,CAAC,CAAC;AACtB,SAAA;AAED,QAAA,IAAI,SAAS,CAAC,OAAO,IAAI,CAAC,cAAc,EAAE;AACxC,YAAA,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACvD,iBAAiB,CAAC,IAAI,CAAC,CAAC;AACzB,SAAA;AACH,KAAC,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,sBAAsB,CAAC,CAAC,CAAC;IAE5G,IAAI,CAAC,cAAc,EAAE;AACnB,QAAA,OAAO,IAAI,CAAC;AACb,KAAA;AAED,IAAA,OAAO,KAAK,CAAA,aAAA,CAAA,KAAA,EAAA,EAAA,GAAG,EAAE,SAAS,GAAI,CAAC;AACjC,CAAC;AAEK,SAAU,iBAAiB,CAAC,QAA4B,EAAA;;AAC5D,IAAA,IAAI,QAAQ,EAAE;AACZ,QAAA,OAAO,QAAQ,CAAC;AACjB,KAAA;AAED,IAAA,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;AACtE,IAAA,MAAM,iBAAiB,GAAG,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,WAA+B,MAAA,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAE,KAAK,CAAC,GAAG,CAAC,MAAA,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,EAAA,GAAI,EAAE,CAAC;AAC5E,IAAA,IAAI,iBAAiB,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE;AACtC,QAAA,OAAO,WAA4B,CAAC;AACrC,KAAA;AAED,IAAA,OAAO,SAAS,CAAC;AACnB;;;;"}
1
+ {"version":3,"file":"GoogleButton.js","sources":["../../src/GoogleButton.tsx"],"sourcesContent":["import { GoogleCredentialResponse } from '@medplum/core';\nimport React, { useEffect, useRef, useState } from 'react';\nimport { useMedplum } from './MedplumProvider';\nimport { createScriptTag } from './utils';\n\ninterface GoogleApi {\n accounts: {\n id: {\n initialize: (args: any) => void;\n renderButton: (parent: HTMLElement, args: any) => void;\n };\n };\n}\n\ndeclare const google: GoogleApi;\n\nexport interface GoogleButtonProps {\n readonly googleClientId?: string;\n readonly handleGoogleCredential: (response: GoogleCredentialResponse) => void;\n}\n\nexport function GoogleButton(props: GoogleButtonProps): JSX.Element | null {\n const medplum = useMedplum();\n const { googleClientId, handleGoogleCredential } = props;\n const parentRef = useRef<HTMLDivElement>(null);\n const [scriptLoaded, setScriptLoaded] = useState<boolean>(typeof google !== 'undefined');\n const [initialized, setInitialized] = useState<boolean>(false);\n const [buttonRendered, setButtonRendered] = useState<boolean>(false);\n\n useEffect(() => {\n if (typeof google === 'undefined') {\n createScriptTag('https://accounts.google.com/gsi/client', () => setScriptLoaded(true));\n return;\n }\n\n if (!initialized) {\n google.accounts.id.initialize({\n client_id: googleClientId,\n callback: handleGoogleCredential,\n });\n setInitialized(true);\n }\n\n if (parentRef.current && !buttonRendered) {\n google.accounts.id.renderButton(parentRef.current, {});\n setButtonRendered(true);\n }\n }, [medplum, googleClientId, initialized, scriptLoaded, parentRef, buttonRendered, handleGoogleCredential]);\n\n if (!googleClientId) {\n return null;\n }\n\n return <div ref={parentRef} />;\n}\n\nexport function getGoogleClientId(clientId: string | undefined): string | undefined {\n if (clientId) {\n return clientId;\n }\n\n const origin = window.location.protocol + '//' + window.location.host;\n const authorizedOrigins = process.env.GOOGLE_AUTH_ORIGINS?.split(',') ?? [];\n if (authorizedOrigins.includes(origin)) {\n return process.env.GOOGLE_CLIENT_ID;\n }\n\n return undefined;\n}\n"],"names":[],"mappings":";;;;AAqBM,SAAU,YAAY,CAAC,KAAwB,EAAA;AACnD,IAAA,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;AAC7B,IAAA,MAAM,EAAE,cAAc,EAAE,sBAAsB,EAAE,GAAG,KAAK,CAAC;AACzD,IAAA,MAAM,SAAS,GAAG,MAAM,CAAiB,IAAI,CAAC,CAAC;AAC/C,IAAA,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAU,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC;IACzF,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC,CAAC;IAC/D,MAAM,CAAC,cAAc,EAAE,iBAAiB,CAAC,GAAG,QAAQ,CAAU,KAAK,CAAC,CAAC;IAErE,SAAS,CAAC,MAAK;AACb,QAAA,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE;YACjC,eAAe,CAAC,wCAAwC,EAAE,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;YACvF,OAAO;AACR,SAAA;QAED,IAAI,CAAC,WAAW,EAAE;AAChB,YAAA,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,UAAU,CAAC;AAC5B,gBAAA,SAAS,EAAE,cAAc;AACzB,gBAAA,QAAQ,EAAE,sBAAsB;AACjC,aAAA,CAAC,CAAC;YACH,cAAc,CAAC,IAAI,CAAC,CAAC;AACtB,SAAA;AAED,QAAA,IAAI,SAAS,CAAC,OAAO,IAAI,CAAC,cAAc,EAAE;AACxC,YAAA,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACvD,iBAAiB,CAAC,IAAI,CAAC,CAAC;AACzB,SAAA;AACH,KAAC,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,sBAAsB,CAAC,CAAC,CAAC;IAE5G,IAAI,CAAC,cAAc,EAAE;AACnB,QAAA,OAAO,IAAI,CAAC;AACb,KAAA;AAED,IAAA,OAAO,KAAK,CAAA,aAAA,CAAA,KAAA,EAAA,EAAA,GAAG,EAAE,SAAS,GAAI,CAAC;AACjC,CAAC;AAEK,SAAU,iBAAiB,CAAC,QAA4B,EAAA;;AAC5D,IAAA,IAAI,QAAQ,EAAE;AACZ,QAAA,OAAO,QAAQ,CAAC;AACjB,KAAA;AAED,IAAA,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;AACtE,IAAA,MAAM,iBAAiB,GAAG,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,8FAA+B,MAAA,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAE,KAAK,CAAC,GAAG,CAAC,MAAA,IAAA,IAAA,EAAA,KAAA,KAAA,CAAA,GAAA,EAAA,GAAI,EAAE,CAAC;AAC5E,IAAA,IAAI,iBAAiB,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE;AACtC,QAAA,OAAO,0EAA4B,CAAC;AACrC,KAAA;AAED,IAAA,OAAO,SAAS,CAAC;AACnB;;;;"}