@macrostrat/column-views 3.3.5 → 3.4.0

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 (62) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/column.cjs +2 -1
  3. package/dist/column.cjs.map +1 -1
  4. package/dist/column.js +2 -1
  5. package/dist/column.js.map +1 -1
  6. package/dist/data-provider/unit-selection.cjs +7 -3
  7. package/dist/data-provider/unit-selection.cjs.map +1 -1
  8. package/dist/data-provider/unit-selection.d.ts +1 -0
  9. package/dist/data-provider/unit-selection.js +7 -3
  10. package/dist/data-provider/unit-selection.js.map +1 -1
  11. package/dist/index.cjs +13 -8
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.js +7 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/prepare-units/helpers.cjs +83 -49
  16. package/dist/prepare-units/helpers.cjs.map +1 -1
  17. package/dist/prepare-units/helpers.d.ts +19 -2
  18. package/dist/prepare-units/helpers.js +84 -50
  19. package/dist/prepare-units/helpers.js.map +1 -1
  20. package/dist/prepare-units/index.cjs.map +1 -1
  21. package/dist/prepare-units/index.js.map +1 -1
  22. package/dist/section.cjs +1 -2
  23. package/dist/section.cjs.map +1 -1
  24. package/dist/section.js +1 -2
  25. package/dist/section.js.map +1 -1
  26. package/dist/unit-details/identifiers.cjs +56 -0
  27. package/dist/unit-details/identifiers.cjs.map +1 -0
  28. package/dist/unit-details/identifiers.d.ts +31 -0
  29. package/dist/unit-details/identifiers.js +54 -0
  30. package/dist/unit-details/identifiers.js.map +1 -0
  31. package/dist/unit-details/index.d.ts +2 -0
  32. package/dist/unit-details/panel.cjs +11 -88
  33. package/dist/unit-details/panel.cjs.map +1 -1
  34. package/dist/unit-details/panel.d.ts +3 -19
  35. package/dist/unit-details/panel.js +10 -87
  36. package/dist/unit-details/panel.js.map +1 -1
  37. package/dist/unit-details/popover.cjs +1 -2
  38. package/dist/unit-details/popover.cjs.map +1 -1
  39. package/dist/unit-details/popover.js +2 -3
  40. package/dist/unit-details/popover.js.map +1 -1
  41. package/dist/unit-details/strat-names.cjs +44 -0
  42. package/dist/unit-details/strat-names.cjs.map +1 -0
  43. package/dist/unit-details/strat-names.d.ts +14 -0
  44. package/dist/unit-details/strat-names.js +42 -0
  45. package/dist/unit-details/strat-names.js.map +1 -0
  46. package/dist/units/index.cjs +20 -6
  47. package/dist/units/index.cjs.map +1 -1
  48. package/dist/units/index.d.ts +11 -20
  49. package/dist/units/index.js +20 -6
  50. package/dist/units/index.js.map +1 -1
  51. package/package.json +14 -14
  52. package/src/column.ts +1 -1
  53. package/src/data-provider/unit-selection.ts +8 -4
  54. package/src/prepare-units/helpers.ts +121 -72
  55. package/src/prepare-units/index.ts +1 -1
  56. package/src/section.ts +0 -1
  57. package/src/unit-details/identifiers.ts +67 -0
  58. package/src/unit-details/index.ts +2 -0
  59. package/src/unit-details/panel.ts +13 -126
  60. package/src/unit-details/popover.ts +2 -7
  61. package/src/unit-details/strat-names.ts +57 -0
  62. package/src/units/index.ts +31 -8
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
1
  # Changelog
2
2
 
3
+ ## [3.4.0] - 2026-05-20 [_changes_](https://github.com/UW-Macrostrat/web-components/compare/@macrostrat/column-views-v3.3.5...@macrostrat/column-views-v3.4.0)
4
+
5
+ ### Minor Changes
6
+
7
+ - Update layout for column units overlaps
8
+ [01048d6f](https://github.com/UW-Macrostrat/web-components/commit/01048d6ffa1335f58334c5c855b86e7a97b3e9c1)
9
+
10
+ ### Patch Changes
11
+
12
+ - Fixed error with strat name viewer
13
+ [239c0292](https://github.com/UW-Macrostrat/web-components/commit/239c0292ce143f642cf05ec934f246798d89a54f)
14
+ - Update blueprintjs dependencies
15
+ [239c0292](https://github.com/UW-Macrostrat/web-components/commit/239c0292ce143f642cf05ec934f246798d89a54f)
16
+ - Improved types for API outputs
17
+ [225ed9a4](https://github.com/UW-Macrostrat/web-components/commit/225ed9a4534b2d1f8be8af6ab7e4035352825614)
18
+ - Updated `jotai` and `jotai-scope` dependencies
19
+ [fb1c5ceb](https://github.com/UW-Macrostrat/web-components/commit/fb1c5ceb37c59aba5ee8dab1cca1d7a09b5b5fb3)
20
+ - Updated dependencies
21
+ [239c0292](https://github.com/UW-Macrostrat/web-components/commit/239c0292ce143f642cf05ec934f246798d89a54f)
22
+ - Updated dependencies
23
+ [239c0292](https://github.com/UW-Macrostrat/web-components/commit/239c0292ce143f642cf05ec934f246798d89a54f)
24
+ - Updated dependencies
25
+ [225ed9a4](https://github.com/UW-Macrostrat/web-components/commit/225ed9a4534b2d1f8be8af6ab7e4035352825614)
26
+ - Updated dependencies
27
+ [01048d6f](https://github.com/UW-Macrostrat/web-components/commit/01048d6ffa1335f58334c5c855b86e7a97b3e9c1)
28
+ - Updated dependencies
29
+ [fb1c5ceb](https://github.com/UW-Macrostrat/web-components/commit/fb1c5ceb37c59aba5ee8dab1cca1d7a09b5b5fb3)
30
+ - @macrostrat/data-provider@1.0.4
31
+ - @macrostrat/column-components@2.0.8
32
+ - @macrostrat/data-components@1.1.10
33
+ - @macrostrat/map-interface@2.2.6
34
+ - @macrostrat/ui-components@5.0.10
35
+ - @macrostrat/mapbox-react@3.2.2
36
+ - @macrostrat/api-types@1.2.0
37
+ - @macrostrat/stratigraphy-utils@1.3.0
38
+ - @macrostrat/map-styles@2.2.3
39
+
3
40
  ## [3.3.5] - 2026-05-19 [_changes_](https://github.com/UW-Macrostrat/web-components/compare/@macrostrat/column-views-v3.3.4...@macrostrat/column-views-v3.3.5)
4
41
 
5
42
  ### Patch Changes
package/dist/column.cjs CHANGED
@@ -16,6 +16,7 @@ const types = require("./prepare-units/types.cjs");
16
16
  const store = require("./data-provider/store.cjs");
17
17
  const popover = require("./unit-details/popover.cjs");
18
18
  const unitSelection = require("./data-provider/unit-selection.cjs");
19
+ const identifiers = require("./unit-details/identifiers.cjs");
19
20
  const panel = require("./unit-details/panel.cjs");
20
21
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
21
22
  const classNames__default = /* @__PURE__ */ _interopDefault(classNames);
@@ -232,7 +233,7 @@ function ColumnBasicInfo({
232
233
  return h("div.column-info", [
233
234
  h("div.column-title-row", [
234
235
  h("h2", data.col_name),
235
- h.if(showColumnID)("h4", h(panel.Identifier, { id: data.col_id }))
236
+ h.if(showColumnID)("h4", h(identifiers.Identifier, { id: data.col_id }))
236
237
  ]),
237
238
  h(dataComponents.DataField, { row: true, label: "Group", value: data.col_group }),
238
239
  h.if(showReferences)(panel.ReferencesField, {
@@ -1 +1 @@
1
- {"version":3,"file":"column.cjs","sources":["../src/column.ts"],"sourcesContent":["import { ColumnAxisType } from \"@macrostrat/column-components\";\nimport { hyperStyled } from \"@macrostrat/hyper\";\nimport {\n Box,\n extractPadding,\n Padding,\n useDarkMode,\n} from \"@macrostrat/ui-components\";\nimport classNames from \"classnames\";\nimport {\n HTMLAttributes,\n useCallback,\n CSSProperties,\n ComponentType,\n} from \"react\";\nimport styles from \"./column.module.sass\";\nimport { UnitComponent } from \"./units\";\nimport {\n UnitKeyboardNavigation,\n useColumnRef,\n useUnitSelectionDispatch,\n} from \"./data-provider\";\n\nimport {\n Identifier,\n ReferencesField,\n UnitSelectionPopover,\n} from \"./unit-details\";\nimport {\n MacrostratColumnDataProvider,\n useCompositeScale,\n useMacrostratColumnData,\n} from \"./data-provider\";\nimport {\n SectionSharedProps,\n CompositeTimescale,\n SectionsColumn,\n} from \"./section\";\nimport { ApproximateHeightAxis, CompositeAgeAxis } from \"./age-axis\";\nimport {\n MergeSectionsMode,\n usePreparedColumnUnits,\n HybridScaleType,\n ColumnHeightScaleOptions,\n} from \"./prepare-units\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport { NonIdealState } from \"@blueprintjs/core\";\nimport { DataField } from \"@macrostrat/data-components\";\nimport { ScaleContinuousNumeric } from \"d3-scale\";\n\nconst h = hyperStyled(styles);\n\ninterface BaseColumnProps extends Omit<\n SectionSharedProps,\n \"unconformityLabels\"\n> {\n className?: string;\n showLabelColumn?: boolean;\n keyboardNavigation?: boolean;\n showLabels?: boolean;\n maxInternalColumns?: number;\n // Timescale properties\n showTimescale?: boolean;\n timescaleLevels?: number | [number, number];\n unconformityLabels?: boolean | UnconformityLabelPlacement;\n onMouseOver?: (\n unit: UnitLong | null,\n height: number | null,\n evt: MouseEvent,\n ) => void;\n}\n\nexport type UnconformityLabelPlacement = \"minimal\" | \"prominent\" | \"none\";\n\nexport interface ColumnProps\n extends Padding, BaseColumnProps, ColumnHeightScaleOptions {\n // Macrostrat units\n units: UnitLong[];\n t_age?: number;\n b_age?: number;\n t_pos?: number;\n b_pos?: number;\n mergeSections?: MergeSectionsMode;\n showUnitPopover?: boolean;\n allowUnitSelection?: boolean;\n selectedUnit?: number | null;\n onUnitSelected?: (unitID: number | null, unit: any) => void;\n // Unconformity height in pixels\n unconformityHeight?: number;\n scale?: ScaleContinuousNumeric<number, number>;\n}\n\nexport function Column(props: ColumnProps) {\n const {\n showUnitPopover = false,\n keyboardNavigation = false,\n mergeSections,\n onUnitSelected,\n selectedUnit,\n children,\n units: rawUnits,\n t_age,\n b_age,\n t_pos,\n b_pos,\n unconformityHeight = 30,\n targetUnitHeight = 20,\n pixelScale,\n minPixelScale = 0.2,\n minSectionHeight = 50,\n collapseSmallUnconformities = true,\n allowUnitSelection = false,\n hybridScale,\n scale,\n axisType,\n ...rest\n } = props;\n\n /* Make pixelScale and targetUnitHeight mutually exclusive. PixelScale implies\n * standardization of scales in all sections */\n let _targetUnitHeight = targetUnitHeight;\n let _minSectionHeight = minSectionHeight;\n let _minPixelScale = minPixelScale;\n if (pixelScale != null) {\n _targetUnitHeight = null;\n _minSectionHeight = 0;\n _minPixelScale = pixelScale;\n }\n\n // Handle special cases for hybrid scales (WIP, we need to regularize this)\n let _axisType = axisType ?? ColumnAxisType.AGE;\n let ageAxisComponent = CompositeAgeAxis;\n if (\n hybridScale?.type === HybridScaleType.ApproximateHeight &&\n _axisType != ColumnAxisType.AGE\n ) {\n // Use approximate height axis for non-age columns if a non-age axis type is requested\n ageAxisComponent = ApproximateHeightAxis;\n _axisType = ColumnAxisType.AGE;\n }\n\n const { sections, units, totalHeight } = usePreparedColumnUnits(rawUnits, {\n axisType: _axisType,\n t_age,\n b_age,\n t_pos,\n b_pos,\n mergeSections,\n targetUnitHeight: _targetUnitHeight,\n unconformityHeight,\n pixelScale,\n minPixelScale: _minPixelScale,\n minSectionHeight: _minSectionHeight,\n collapseSmallUnconformities,\n // TODO: consider unifying scale and hybridScale options\n scale,\n hybridScale,\n });\n\n if (sections.length === 0) {\n return h(\n \"div.column-container.empty\",\n h(NonIdealState, {\n title: \"Empty column\",\n description: \"No sections found in this column.\",\n icon: \"warning-sign\",\n }),\n );\n }\n\n return h(\n MacrostratColumnDataProvider,\n {\n units,\n sections,\n totalHeight,\n axisType: _axisType,\n allowUnitSelection: showUnitPopover || allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n },\n h(ColumnInner, { ageAxisComponent, ...rest }, [\n children,\n h.if(showUnitPopover)(UnitSelectionPopover),\n h.if(keyboardNavigation)(UnitKeyboardNavigation, { units }),\n ]),\n );\n}\n\ninterface ColumnInnerProps extends BaseColumnProps {\n ageAxisComponent?: ComponentType;\n}\n\nfunction ColumnInner(props: ColumnInnerProps) {\n const padding = extractPadding(props);\n\n // TODO: integrate padding vars more closely with the rest of the spacing (right now padding is a bit ad-hoc)\n const paddingVars: any = {\n \"--column-padding-top\": `${padding.paddingTop}px`,\n \"--column-padding-bottom\": `${padding.paddingBottom}px`,\n \"--column-padding-left\": `${padding.paddingLeft}px`,\n \"--column-padding-right\": `${padding.paddingRight}px`,\n };\n\n const {\n unitComponent = UnitComponent,\n unconformityLabels = \"minimal\",\n showLabels = true,\n width: _width = 300,\n columnWidth: _columnWidth = 150,\n showLabelColumn: _showLabelColumn = true,\n className,\n clipUnits = false,\n children,\n showTimescale,\n timescaleLevels,\n maxInternalColumns,\n onMouseOver,\n ageAxisComponent = CompositeAgeAxis,\n } = props;\n\n const { axisType } = useMacrostratColumnData();\n\n const columnRef = useColumnRef();\n\n // Coalesce unconformity label setting to a boolean\n let _timescaleUnconformityLabels = false;\n let _sectionUnconformityLabels = false;\n if (unconformityLabels === true || unconformityLabels === \"prominent\") {\n _sectionUnconformityLabels = true;\n } else if (unconformityLabels === \"minimal\") {\n _timescaleUnconformityLabels = true;\n }\n\n let width = _width;\n let columnWidth = _columnWidth;\n if (columnWidth > width) {\n columnWidth = width;\n }\n let showLabelColumn = _showLabelColumn;\n if (columnWidth > width - 10) {\n showLabelColumn = false;\n }\n\n let _showTimescale = showTimescale ?? true;\n if (timescaleLevels != null) {\n _showTimescale = true;\n }\n _showTimescale = axisType == ColumnAxisType.AGE && _showTimescale;\n\n return h(\n ColumnContainer,\n {\n ...useMouseEventHandlers(onMouseOver),\n style: paddingVars,\n className,\n },\n h(\"div.column\", { ref: columnRef }, [\n h(ageAxisComponent),\n h.if(_showTimescale)(CompositeTimescale, {\n levels: timescaleLevels,\n unconformityLabels: _timescaleUnconformityLabels,\n }),\n h(SectionsColumn, {\n unitComponent,\n showLabels,\n width,\n columnWidth,\n showLabelColumn,\n clipUnits,\n unconformityLabels: _sectionUnconformityLabels,\n maxInternalColumns,\n }),\n children,\n ]),\n );\n}\n\ntype ColumnMouseOverHandler = (\n unit: UnitLong | null,\n height: number | null,\n evt: MouseEvent,\n) => void;\n\nfunction useMouseEventHandlers(\n _onMouseOver: ColumnMouseOverHandler | null = null,\n) {\n /** Click event handler */\n\n // Click handler for unit selection\n const dispatch = useUnitSelectionDispatch();\n const onClick = useCallback(\n (evt) => {\n dispatch?.(null, null);\n },\n [dispatch],\n );\n\n /** Hover event handlers */\n const scale = useCompositeScale();\n\n const onMouseOver = useCallback(\n (evt) => {\n const height = scale.invert(\n evt.clientY - evt.currentTarget.getBoundingClientRect().top,\n );\n _onMouseOver?.(null, height, evt);\n },\n [scale, _onMouseOver],\n );\n\n const onMouseOut = useCallback(() => {\n _onMouseOver?.(null, null, null);\n }, [_onMouseOver]);\n\n if (_onMouseOver == null) {\n return {\n onClick,\n onMouseOver: undefined,\n onMouseMove: undefined,\n onMouseOut: undefined,\n };\n }\n\n return { onMouseOver, onMouseMove: onMouseOver, onMouseOut, onClick };\n}\n\nexport interface ColumnContainerProps extends HTMLAttributes<HTMLDivElement> {\n className?: string;\n style?: CSSProperties;\n}\n\nexport function ColumnContainer(props: ColumnContainerProps) {\n const { className, ...rest } = props;\n const darkMode = useDarkMode();\n\n return h(Box, {\n className: classNames(\n className,\n {\n \"dark-mode\": darkMode?.isEnabled ?? false,\n },\n \"column-container\",\n ),\n ...rest,\n });\n}\n\nexport function ColumnBasicInfo({\n data,\n showColumnID = true,\n showReferences = true,\n}) {\n if (data == null) return null;\n return h(\"div.column-info\", [\n h(\"div.column-title-row\", [\n h(\"h2\", data.col_name),\n h.if(showColumnID)(\"h4\", h(Identifier, { id: data.col_id })),\n ]),\n h(DataField, { row: true, label: \"Group\", value: data.col_group }),\n h.if(showReferences)(ReferencesField, {\n refs: data.refs,\n inline: false,\n row: false,\n className: \"column-refs\",\n }),\n ]);\n}\n"],"names":["hyperStyled","styles","ColumnAxisType","CompositeAgeAxis","HybridScaleType","ApproximateHeightAxis","usePreparedColumnUnits","NonIdealState","MacrostratColumnDataProvider","UnitSelectionPopover","UnitKeyboardNavigation","extractPadding","UnitComponent","useMacrostratColumnData","useColumnRef","CompositeTimescale","SectionsColumn","useUnitSelectionDispatch","useCallback","useCompositeScale","useDarkMode","Box","classNames","Identifier","DataField","ReferencesField"],"mappings":"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"}
1
+ {"version":3,"file":"column.cjs","sources":["../src/column.ts"],"sourcesContent":["import { ColumnAxisType } from \"@macrostrat/column-components\";\nimport { hyperStyled } from \"@macrostrat/hyper\";\nimport {\n Box,\n extractPadding,\n Padding,\n useDarkMode,\n} from \"@macrostrat/ui-components\";\nimport classNames from \"classnames\";\nimport {\n HTMLAttributes,\n useCallback,\n CSSProperties,\n ComponentType,\n} from \"react\";\nimport styles from \"./column.module.sass\";\nimport { UnitComponent } from \"./units\";\nimport {\n UnitKeyboardNavigation,\n useColumnRef,\n useUnitSelectionDispatch,\n} from \"./data-provider\";\n\nimport {\n Identifier,\n ReferencesField,\n UnitSelectionPopover,\n} from \"./unit-details\";\nimport {\n MacrostratColumnDataProvider,\n useCompositeScale,\n useMacrostratColumnData,\n} from \"./data-provider\";\nimport {\n SectionSharedProps,\n CompositeTimescale,\n SectionsColumn,\n} from \"./section\";\nimport { ApproximateHeightAxis, CompositeAgeAxis } from \"./age-axis\";\nimport {\n MergeSectionsMode,\n usePreparedColumnUnits,\n HybridScaleType,\n ColumnHeightScaleOptions,\n} from \"./prepare-units\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport { NonIdealState } from \"@blueprintjs/core\";\nimport { DataField } from \"@macrostrat/data-components\";\nimport { ScaleContinuousNumeric } from \"d3-scale\";\n\nconst h = hyperStyled(styles);\n\ninterface BaseColumnProps extends Omit<\n SectionSharedProps,\n \"unconformityLabels\"\n> {\n className?: string;\n showLabelColumn?: boolean;\n keyboardNavigation?: boolean;\n showLabels?: boolean;\n maxInternalColumns?: number;\n // Timescale properties\n showTimescale?: boolean;\n timescaleLevels?: number | [number, number];\n unconformityLabels?: boolean | UnconformityLabelPlacement;\n onMouseOver?: (\n unit: UnitLong | null,\n height: number | null,\n evt: MouseEvent,\n ) => void;\n}\n\nexport type UnconformityLabelPlacement = \"minimal\" | \"prominent\" | \"none\";\n\nexport interface ColumnProps\n extends Padding, BaseColumnProps, ColumnHeightScaleOptions {\n // Macrostrat units\n units: UnitLong[];\n t_age?: number;\n b_age?: number;\n t_pos?: number;\n b_pos?: number;\n mergeSections?: MergeSectionsMode;\n showUnitPopover?: boolean;\n allowUnitSelection?: boolean;\n selectedUnit?: number | null;\n onUnitSelected?: (unitID: number | null, unit: any) => void;\n // Unconformity height in pixels\n unconformityHeight?: number;\n scale?: ScaleContinuousNumeric<number, number>;\n}\n\nexport function Column(props: ColumnProps) {\n const {\n showUnitPopover = false,\n keyboardNavigation = false,\n mergeSections,\n onUnitSelected,\n selectedUnit,\n children,\n units: rawUnits,\n t_age,\n b_age,\n t_pos,\n b_pos,\n unconformityHeight = 30,\n targetUnitHeight = 20,\n pixelScale,\n minPixelScale = 0.2,\n minSectionHeight = 50,\n collapseSmallUnconformities = true,\n allowUnitSelection = false,\n hybridScale,\n scale,\n axisType,\n ...rest\n } = props;\n\n /* Make pixelScale and targetUnitHeight mutually exclusive. PixelScale implies\n * standardization of scales in all sections */\n let _targetUnitHeight: number | null = targetUnitHeight;\n let _minSectionHeight = minSectionHeight;\n let _minPixelScale = minPixelScale;\n if (pixelScale != null) {\n _targetUnitHeight = null;\n _minSectionHeight = 0;\n _minPixelScale = pixelScale;\n }\n\n // Handle special cases for hybrid scales (WIP, we need to regularize this)\n let _axisType = axisType ?? ColumnAxisType.AGE;\n let ageAxisComponent = CompositeAgeAxis;\n if (\n hybridScale?.type === HybridScaleType.ApproximateHeight &&\n _axisType != ColumnAxisType.AGE\n ) {\n // Use approximate height axis for non-age columns if a non-age axis type is requested\n ageAxisComponent = ApproximateHeightAxis;\n _axisType = ColumnAxisType.AGE;\n }\n\n const { sections, units, totalHeight } = usePreparedColumnUnits(rawUnits, {\n axisType: _axisType,\n t_age,\n b_age,\n t_pos,\n b_pos,\n mergeSections,\n targetUnitHeight: _targetUnitHeight,\n unconformityHeight,\n pixelScale,\n minPixelScale: _minPixelScale,\n minSectionHeight: _minSectionHeight,\n collapseSmallUnconformities,\n // TODO: consider unifying scale and hybridScale options\n scale,\n hybridScale,\n });\n\n if (sections.length === 0) {\n return h(\n \"div.column-container.empty\",\n h(NonIdealState, {\n title: \"Empty column\",\n description: \"No sections found in this column.\",\n icon: \"warning-sign\",\n }),\n );\n }\n\n return h(\n MacrostratColumnDataProvider,\n {\n units,\n sections,\n totalHeight,\n axisType: _axisType,\n allowUnitSelection: showUnitPopover || allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n },\n h(ColumnInner, { ageAxisComponent, ...rest }, [\n children,\n h.if(showUnitPopover)(UnitSelectionPopover),\n h.if(keyboardNavigation)(UnitKeyboardNavigation, { units }),\n ]),\n );\n}\n\ninterface ColumnInnerProps extends BaseColumnProps {\n ageAxisComponent?: ComponentType;\n}\n\nfunction ColumnInner(props: ColumnInnerProps) {\n const padding = extractPadding(props);\n\n // TODO: integrate padding vars more closely with the rest of the spacing (right now padding is a bit ad-hoc)\n const paddingVars: any = {\n \"--column-padding-top\": `${padding.paddingTop}px`,\n \"--column-padding-bottom\": `${padding.paddingBottom}px`,\n \"--column-padding-left\": `${padding.paddingLeft}px`,\n \"--column-padding-right\": `${padding.paddingRight}px`,\n };\n\n const {\n unitComponent = UnitComponent,\n unconformityLabels = \"minimal\",\n showLabels = true,\n width: _width = 300,\n columnWidth: _columnWidth = 150,\n showLabelColumn: _showLabelColumn = true,\n className,\n clipUnits = false,\n children,\n showTimescale,\n timescaleLevels,\n maxInternalColumns,\n onMouseOver,\n ageAxisComponent = CompositeAgeAxis,\n } = props;\n\n const { axisType } = useMacrostratColumnData();\n\n const columnRef = useColumnRef();\n\n // Coalesce unconformity label setting to a boolean\n let _timescaleUnconformityLabels = false;\n let _sectionUnconformityLabels = false;\n if (unconformityLabels === true || unconformityLabels === \"prominent\") {\n _sectionUnconformityLabels = true;\n } else if (unconformityLabels === \"minimal\") {\n _timescaleUnconformityLabels = true;\n }\n\n let width = _width;\n let columnWidth = _columnWidth;\n if (columnWidth > width) {\n columnWidth = width;\n }\n let showLabelColumn = _showLabelColumn;\n if (columnWidth > width - 10) {\n showLabelColumn = false;\n }\n\n let _showTimescale = showTimescale ?? true;\n if (timescaleLevels != null) {\n _showTimescale = true;\n }\n _showTimescale = axisType == ColumnAxisType.AGE && _showTimescale;\n\n return h(\n ColumnContainer,\n {\n ...useMouseEventHandlers(onMouseOver),\n style: paddingVars,\n className,\n },\n h(\"div.column\", { ref: columnRef }, [\n h(ageAxisComponent),\n h.if(_showTimescale)(CompositeTimescale, {\n levels: timescaleLevels,\n unconformityLabels: _timescaleUnconformityLabels,\n }),\n h(SectionsColumn, {\n unitComponent,\n showLabels,\n width,\n columnWidth,\n showLabelColumn,\n clipUnits,\n unconformityLabels: _sectionUnconformityLabels,\n maxInternalColumns,\n }),\n children,\n ]),\n );\n}\n\ntype ColumnMouseOverHandler = (\n unit: UnitLong | null,\n height: number | null,\n evt: MouseEvent,\n) => void;\n\nfunction useMouseEventHandlers(\n _onMouseOver: ColumnMouseOverHandler | null = null,\n) {\n /** Click event handler */\n\n // Click handler for unit selection\n const dispatch = useUnitSelectionDispatch();\n const onClick = useCallback(\n (evt) => {\n dispatch?.(null, null);\n },\n [dispatch],\n );\n\n /** Hover event handlers */\n const scale = useCompositeScale();\n\n const onMouseOver = useCallback(\n (evt) => {\n const height = scale.invert(\n evt.clientY - evt.currentTarget.getBoundingClientRect().top,\n );\n _onMouseOver?.(null, height, evt);\n },\n [scale, _onMouseOver],\n );\n\n const onMouseOut = useCallback(() => {\n _onMouseOver?.(null, null, null);\n }, [_onMouseOver]);\n\n if (_onMouseOver == null) {\n return {\n onClick,\n onMouseOver: undefined,\n onMouseMove: undefined,\n onMouseOut: undefined,\n };\n }\n\n return { onMouseOver, onMouseMove: onMouseOver, onMouseOut, onClick };\n}\n\nexport interface ColumnContainerProps extends HTMLAttributes<HTMLDivElement> {\n className?: string;\n style?: CSSProperties;\n}\n\nexport function ColumnContainer(props: ColumnContainerProps) {\n const { className, ...rest } = props;\n const darkMode = useDarkMode();\n\n return h(Box, {\n className: classNames(\n className,\n {\n \"dark-mode\": darkMode?.isEnabled ?? false,\n },\n \"column-container\",\n ),\n ...rest,\n });\n}\n\nexport function ColumnBasicInfo({\n data,\n showColumnID = true,\n showReferences = true,\n}) {\n if (data == null) return null;\n return h(\"div.column-info\", [\n h(\"div.column-title-row\", [\n h(\"h2\", data.col_name),\n h.if(showColumnID)(\"h4\", h(Identifier, { id: data.col_id })),\n ]),\n h(DataField, { row: true, label: \"Group\", value: data.col_group }),\n h.if(showReferences)(ReferencesField, {\n refs: data.refs,\n inline: false,\n row: false,\n className: \"column-refs\",\n }),\n ]);\n}\n"],"names":["hyperStyled","styles","ColumnAxisType","CompositeAgeAxis","HybridScaleType","ApproximateHeightAxis","usePreparedColumnUnits","NonIdealState","MacrostratColumnDataProvider","UnitSelectionPopover","UnitKeyboardNavigation","extractPadding","UnitComponent","useMacrostratColumnData","useColumnRef","CompositeTimescale","SectionsColumn","useUnitSelectionDispatch","useCallback","useCompositeScale","useDarkMode","Box","classNames","Identifier","DataField","ReferencesField"],"mappings":"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"}
package/dist/column.js CHANGED
@@ -14,7 +14,8 @@ import { HybridScaleType } from "./prepare-units/types.js";
14
14
  import { MacrostratColumnDataProvider, useMacrostratColumnData, useCompositeScale } from "./data-provider/store.js";
15
15
  import { UnitSelectionPopover } from "./unit-details/popover.js";
16
16
  import { UnitKeyboardNavigation, useColumnRef, useUnitSelectionDispatch } from "./data-provider/unit-selection.js";
17
- import { Identifier, ReferencesField } from "./unit-details/panel.js";
17
+ import { Identifier } from "./unit-details/identifiers.js";
18
+ import { ReferencesField } from "./unit-details/panel.js";
18
19
  const h = hyperStyled(h$1);
19
20
  function Column(props) {
20
21
  const {
@@ -1 +1 @@
1
- {"version":3,"file":"column.js","sources":["../src/column.ts"],"sourcesContent":["import { ColumnAxisType } from \"@macrostrat/column-components\";\nimport { hyperStyled } from \"@macrostrat/hyper\";\nimport {\n Box,\n extractPadding,\n Padding,\n useDarkMode,\n} from \"@macrostrat/ui-components\";\nimport classNames from \"classnames\";\nimport {\n HTMLAttributes,\n useCallback,\n CSSProperties,\n ComponentType,\n} from \"react\";\nimport styles from \"./column.module.sass\";\nimport { UnitComponent } from \"./units\";\nimport {\n UnitKeyboardNavigation,\n useColumnRef,\n useUnitSelectionDispatch,\n} from \"./data-provider\";\n\nimport {\n Identifier,\n ReferencesField,\n UnitSelectionPopover,\n} from \"./unit-details\";\nimport {\n MacrostratColumnDataProvider,\n useCompositeScale,\n useMacrostratColumnData,\n} from \"./data-provider\";\nimport {\n SectionSharedProps,\n CompositeTimescale,\n SectionsColumn,\n} from \"./section\";\nimport { ApproximateHeightAxis, CompositeAgeAxis } from \"./age-axis\";\nimport {\n MergeSectionsMode,\n usePreparedColumnUnits,\n HybridScaleType,\n ColumnHeightScaleOptions,\n} from \"./prepare-units\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport { NonIdealState } from \"@blueprintjs/core\";\nimport { DataField } from \"@macrostrat/data-components\";\nimport { ScaleContinuousNumeric } from \"d3-scale\";\n\nconst h = hyperStyled(styles);\n\ninterface BaseColumnProps extends Omit<\n SectionSharedProps,\n \"unconformityLabels\"\n> {\n className?: string;\n showLabelColumn?: boolean;\n keyboardNavigation?: boolean;\n showLabels?: boolean;\n maxInternalColumns?: number;\n // Timescale properties\n showTimescale?: boolean;\n timescaleLevels?: number | [number, number];\n unconformityLabels?: boolean | UnconformityLabelPlacement;\n onMouseOver?: (\n unit: UnitLong | null,\n height: number | null,\n evt: MouseEvent,\n ) => void;\n}\n\nexport type UnconformityLabelPlacement = \"minimal\" | \"prominent\" | \"none\";\n\nexport interface ColumnProps\n extends Padding, BaseColumnProps, ColumnHeightScaleOptions {\n // Macrostrat units\n units: UnitLong[];\n t_age?: number;\n b_age?: number;\n t_pos?: number;\n b_pos?: number;\n mergeSections?: MergeSectionsMode;\n showUnitPopover?: boolean;\n allowUnitSelection?: boolean;\n selectedUnit?: number | null;\n onUnitSelected?: (unitID: number | null, unit: any) => void;\n // Unconformity height in pixels\n unconformityHeight?: number;\n scale?: ScaleContinuousNumeric<number, number>;\n}\n\nexport function Column(props: ColumnProps) {\n const {\n showUnitPopover = false,\n keyboardNavigation = false,\n mergeSections,\n onUnitSelected,\n selectedUnit,\n children,\n units: rawUnits,\n t_age,\n b_age,\n t_pos,\n b_pos,\n unconformityHeight = 30,\n targetUnitHeight = 20,\n pixelScale,\n minPixelScale = 0.2,\n minSectionHeight = 50,\n collapseSmallUnconformities = true,\n allowUnitSelection = false,\n hybridScale,\n scale,\n axisType,\n ...rest\n } = props;\n\n /* Make pixelScale and targetUnitHeight mutually exclusive. PixelScale implies\n * standardization of scales in all sections */\n let _targetUnitHeight = targetUnitHeight;\n let _minSectionHeight = minSectionHeight;\n let _minPixelScale = minPixelScale;\n if (pixelScale != null) {\n _targetUnitHeight = null;\n _minSectionHeight = 0;\n _minPixelScale = pixelScale;\n }\n\n // Handle special cases for hybrid scales (WIP, we need to regularize this)\n let _axisType = axisType ?? ColumnAxisType.AGE;\n let ageAxisComponent = CompositeAgeAxis;\n if (\n hybridScale?.type === HybridScaleType.ApproximateHeight &&\n _axisType != ColumnAxisType.AGE\n ) {\n // Use approximate height axis for non-age columns if a non-age axis type is requested\n ageAxisComponent = ApproximateHeightAxis;\n _axisType = ColumnAxisType.AGE;\n }\n\n const { sections, units, totalHeight } = usePreparedColumnUnits(rawUnits, {\n axisType: _axisType,\n t_age,\n b_age,\n t_pos,\n b_pos,\n mergeSections,\n targetUnitHeight: _targetUnitHeight,\n unconformityHeight,\n pixelScale,\n minPixelScale: _minPixelScale,\n minSectionHeight: _minSectionHeight,\n collapseSmallUnconformities,\n // TODO: consider unifying scale and hybridScale options\n scale,\n hybridScale,\n });\n\n if (sections.length === 0) {\n return h(\n \"div.column-container.empty\",\n h(NonIdealState, {\n title: \"Empty column\",\n description: \"No sections found in this column.\",\n icon: \"warning-sign\",\n }),\n );\n }\n\n return h(\n MacrostratColumnDataProvider,\n {\n units,\n sections,\n totalHeight,\n axisType: _axisType,\n allowUnitSelection: showUnitPopover || allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n },\n h(ColumnInner, { ageAxisComponent, ...rest }, [\n children,\n h.if(showUnitPopover)(UnitSelectionPopover),\n h.if(keyboardNavigation)(UnitKeyboardNavigation, { units }),\n ]),\n );\n}\n\ninterface ColumnInnerProps extends BaseColumnProps {\n ageAxisComponent?: ComponentType;\n}\n\nfunction ColumnInner(props: ColumnInnerProps) {\n const padding = extractPadding(props);\n\n // TODO: integrate padding vars more closely with the rest of the spacing (right now padding is a bit ad-hoc)\n const paddingVars: any = {\n \"--column-padding-top\": `${padding.paddingTop}px`,\n \"--column-padding-bottom\": `${padding.paddingBottom}px`,\n \"--column-padding-left\": `${padding.paddingLeft}px`,\n \"--column-padding-right\": `${padding.paddingRight}px`,\n };\n\n const {\n unitComponent = UnitComponent,\n unconformityLabels = \"minimal\",\n showLabels = true,\n width: _width = 300,\n columnWidth: _columnWidth = 150,\n showLabelColumn: _showLabelColumn = true,\n className,\n clipUnits = false,\n children,\n showTimescale,\n timescaleLevels,\n maxInternalColumns,\n onMouseOver,\n ageAxisComponent = CompositeAgeAxis,\n } = props;\n\n const { axisType } = useMacrostratColumnData();\n\n const columnRef = useColumnRef();\n\n // Coalesce unconformity label setting to a boolean\n let _timescaleUnconformityLabels = false;\n let _sectionUnconformityLabels = false;\n if (unconformityLabels === true || unconformityLabels === \"prominent\") {\n _sectionUnconformityLabels = true;\n } else if (unconformityLabels === \"minimal\") {\n _timescaleUnconformityLabels = true;\n }\n\n let width = _width;\n let columnWidth = _columnWidth;\n if (columnWidth > width) {\n columnWidth = width;\n }\n let showLabelColumn = _showLabelColumn;\n if (columnWidth > width - 10) {\n showLabelColumn = false;\n }\n\n let _showTimescale = showTimescale ?? true;\n if (timescaleLevels != null) {\n _showTimescale = true;\n }\n _showTimescale = axisType == ColumnAxisType.AGE && _showTimescale;\n\n return h(\n ColumnContainer,\n {\n ...useMouseEventHandlers(onMouseOver),\n style: paddingVars,\n className,\n },\n h(\"div.column\", { ref: columnRef }, [\n h(ageAxisComponent),\n h.if(_showTimescale)(CompositeTimescale, {\n levels: timescaleLevels,\n unconformityLabels: _timescaleUnconformityLabels,\n }),\n h(SectionsColumn, {\n unitComponent,\n showLabels,\n width,\n columnWidth,\n showLabelColumn,\n clipUnits,\n unconformityLabels: _sectionUnconformityLabels,\n maxInternalColumns,\n }),\n children,\n ]),\n );\n}\n\ntype ColumnMouseOverHandler = (\n unit: UnitLong | null,\n height: number | null,\n evt: MouseEvent,\n) => void;\n\nfunction useMouseEventHandlers(\n _onMouseOver: ColumnMouseOverHandler | null = null,\n) {\n /** Click event handler */\n\n // Click handler for unit selection\n const dispatch = useUnitSelectionDispatch();\n const onClick = useCallback(\n (evt) => {\n dispatch?.(null, null);\n },\n [dispatch],\n );\n\n /** Hover event handlers */\n const scale = useCompositeScale();\n\n const onMouseOver = useCallback(\n (evt) => {\n const height = scale.invert(\n evt.clientY - evt.currentTarget.getBoundingClientRect().top,\n );\n _onMouseOver?.(null, height, evt);\n },\n [scale, _onMouseOver],\n );\n\n const onMouseOut = useCallback(() => {\n _onMouseOver?.(null, null, null);\n }, [_onMouseOver]);\n\n if (_onMouseOver == null) {\n return {\n onClick,\n onMouseOver: undefined,\n onMouseMove: undefined,\n onMouseOut: undefined,\n };\n }\n\n return { onMouseOver, onMouseMove: onMouseOver, onMouseOut, onClick };\n}\n\nexport interface ColumnContainerProps extends HTMLAttributes<HTMLDivElement> {\n className?: string;\n style?: CSSProperties;\n}\n\nexport function ColumnContainer(props: ColumnContainerProps) {\n const { className, ...rest } = props;\n const darkMode = useDarkMode();\n\n return h(Box, {\n className: classNames(\n className,\n {\n \"dark-mode\": darkMode?.isEnabled ?? false,\n },\n \"column-container\",\n ),\n ...rest,\n });\n}\n\nexport function ColumnBasicInfo({\n data,\n showColumnID = true,\n showReferences = true,\n}) {\n if (data == null) return null;\n return h(\"div.column-info\", [\n h(\"div.column-title-row\", [\n h(\"h2\", data.col_name),\n h.if(showColumnID)(\"h4\", h(Identifier, { id: data.col_id })),\n ]),\n h(DataField, { row: true, label: \"Group\", value: data.col_group }),\n h.if(showReferences)(ReferencesField, {\n refs: data.refs,\n inline: false,\n row: false,\n className: \"column-refs\",\n }),\n ]);\n}\n"],"names":["styles"],"mappings":"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"}
1
+ {"version":3,"file":"column.js","sources":["../src/column.ts"],"sourcesContent":["import { ColumnAxisType } from \"@macrostrat/column-components\";\nimport { hyperStyled } from \"@macrostrat/hyper\";\nimport {\n Box,\n extractPadding,\n Padding,\n useDarkMode,\n} from \"@macrostrat/ui-components\";\nimport classNames from \"classnames\";\nimport {\n HTMLAttributes,\n useCallback,\n CSSProperties,\n ComponentType,\n} from \"react\";\nimport styles from \"./column.module.sass\";\nimport { UnitComponent } from \"./units\";\nimport {\n UnitKeyboardNavigation,\n useColumnRef,\n useUnitSelectionDispatch,\n} from \"./data-provider\";\n\nimport {\n Identifier,\n ReferencesField,\n UnitSelectionPopover,\n} from \"./unit-details\";\nimport {\n MacrostratColumnDataProvider,\n useCompositeScale,\n useMacrostratColumnData,\n} from \"./data-provider\";\nimport {\n SectionSharedProps,\n CompositeTimescale,\n SectionsColumn,\n} from \"./section\";\nimport { ApproximateHeightAxis, CompositeAgeAxis } from \"./age-axis\";\nimport {\n MergeSectionsMode,\n usePreparedColumnUnits,\n HybridScaleType,\n ColumnHeightScaleOptions,\n} from \"./prepare-units\";\nimport { UnitLong } from \"@macrostrat/api-types\";\nimport { NonIdealState } from \"@blueprintjs/core\";\nimport { DataField } from \"@macrostrat/data-components\";\nimport { ScaleContinuousNumeric } from \"d3-scale\";\n\nconst h = hyperStyled(styles);\n\ninterface BaseColumnProps extends Omit<\n SectionSharedProps,\n \"unconformityLabels\"\n> {\n className?: string;\n showLabelColumn?: boolean;\n keyboardNavigation?: boolean;\n showLabels?: boolean;\n maxInternalColumns?: number;\n // Timescale properties\n showTimescale?: boolean;\n timescaleLevels?: number | [number, number];\n unconformityLabels?: boolean | UnconformityLabelPlacement;\n onMouseOver?: (\n unit: UnitLong | null,\n height: number | null,\n evt: MouseEvent,\n ) => void;\n}\n\nexport type UnconformityLabelPlacement = \"minimal\" | \"prominent\" | \"none\";\n\nexport interface ColumnProps\n extends Padding, BaseColumnProps, ColumnHeightScaleOptions {\n // Macrostrat units\n units: UnitLong[];\n t_age?: number;\n b_age?: number;\n t_pos?: number;\n b_pos?: number;\n mergeSections?: MergeSectionsMode;\n showUnitPopover?: boolean;\n allowUnitSelection?: boolean;\n selectedUnit?: number | null;\n onUnitSelected?: (unitID: number | null, unit: any) => void;\n // Unconformity height in pixels\n unconformityHeight?: number;\n scale?: ScaleContinuousNumeric<number, number>;\n}\n\nexport function Column(props: ColumnProps) {\n const {\n showUnitPopover = false,\n keyboardNavigation = false,\n mergeSections,\n onUnitSelected,\n selectedUnit,\n children,\n units: rawUnits,\n t_age,\n b_age,\n t_pos,\n b_pos,\n unconformityHeight = 30,\n targetUnitHeight = 20,\n pixelScale,\n minPixelScale = 0.2,\n minSectionHeight = 50,\n collapseSmallUnconformities = true,\n allowUnitSelection = false,\n hybridScale,\n scale,\n axisType,\n ...rest\n } = props;\n\n /* Make pixelScale and targetUnitHeight mutually exclusive. PixelScale implies\n * standardization of scales in all sections */\n let _targetUnitHeight: number | null = targetUnitHeight;\n let _minSectionHeight = minSectionHeight;\n let _minPixelScale = minPixelScale;\n if (pixelScale != null) {\n _targetUnitHeight = null;\n _minSectionHeight = 0;\n _minPixelScale = pixelScale;\n }\n\n // Handle special cases for hybrid scales (WIP, we need to regularize this)\n let _axisType = axisType ?? ColumnAxisType.AGE;\n let ageAxisComponent = CompositeAgeAxis;\n if (\n hybridScale?.type === HybridScaleType.ApproximateHeight &&\n _axisType != ColumnAxisType.AGE\n ) {\n // Use approximate height axis for non-age columns if a non-age axis type is requested\n ageAxisComponent = ApproximateHeightAxis;\n _axisType = ColumnAxisType.AGE;\n }\n\n const { sections, units, totalHeight } = usePreparedColumnUnits(rawUnits, {\n axisType: _axisType,\n t_age,\n b_age,\n t_pos,\n b_pos,\n mergeSections,\n targetUnitHeight: _targetUnitHeight,\n unconformityHeight,\n pixelScale,\n minPixelScale: _minPixelScale,\n minSectionHeight: _minSectionHeight,\n collapseSmallUnconformities,\n // TODO: consider unifying scale and hybridScale options\n scale,\n hybridScale,\n });\n\n if (sections.length === 0) {\n return h(\n \"div.column-container.empty\",\n h(NonIdealState, {\n title: \"Empty column\",\n description: \"No sections found in this column.\",\n icon: \"warning-sign\",\n }),\n );\n }\n\n return h(\n MacrostratColumnDataProvider,\n {\n units,\n sections,\n totalHeight,\n axisType: _axisType,\n allowUnitSelection: showUnitPopover || allowUnitSelection,\n onUnitSelected,\n selectedUnit,\n },\n h(ColumnInner, { ageAxisComponent, ...rest }, [\n children,\n h.if(showUnitPopover)(UnitSelectionPopover),\n h.if(keyboardNavigation)(UnitKeyboardNavigation, { units }),\n ]),\n );\n}\n\ninterface ColumnInnerProps extends BaseColumnProps {\n ageAxisComponent?: ComponentType;\n}\n\nfunction ColumnInner(props: ColumnInnerProps) {\n const padding = extractPadding(props);\n\n // TODO: integrate padding vars more closely with the rest of the spacing (right now padding is a bit ad-hoc)\n const paddingVars: any = {\n \"--column-padding-top\": `${padding.paddingTop}px`,\n \"--column-padding-bottom\": `${padding.paddingBottom}px`,\n \"--column-padding-left\": `${padding.paddingLeft}px`,\n \"--column-padding-right\": `${padding.paddingRight}px`,\n };\n\n const {\n unitComponent = UnitComponent,\n unconformityLabels = \"minimal\",\n showLabels = true,\n width: _width = 300,\n columnWidth: _columnWidth = 150,\n showLabelColumn: _showLabelColumn = true,\n className,\n clipUnits = false,\n children,\n showTimescale,\n timescaleLevels,\n maxInternalColumns,\n onMouseOver,\n ageAxisComponent = CompositeAgeAxis,\n } = props;\n\n const { axisType } = useMacrostratColumnData();\n\n const columnRef = useColumnRef();\n\n // Coalesce unconformity label setting to a boolean\n let _timescaleUnconformityLabels = false;\n let _sectionUnconformityLabels = false;\n if (unconformityLabels === true || unconformityLabels === \"prominent\") {\n _sectionUnconformityLabels = true;\n } else if (unconformityLabels === \"minimal\") {\n _timescaleUnconformityLabels = true;\n }\n\n let width = _width;\n let columnWidth = _columnWidth;\n if (columnWidth > width) {\n columnWidth = width;\n }\n let showLabelColumn = _showLabelColumn;\n if (columnWidth > width - 10) {\n showLabelColumn = false;\n }\n\n let _showTimescale = showTimescale ?? true;\n if (timescaleLevels != null) {\n _showTimescale = true;\n }\n _showTimescale = axisType == ColumnAxisType.AGE && _showTimescale;\n\n return h(\n ColumnContainer,\n {\n ...useMouseEventHandlers(onMouseOver),\n style: paddingVars,\n className,\n },\n h(\"div.column\", { ref: columnRef }, [\n h(ageAxisComponent),\n h.if(_showTimescale)(CompositeTimescale, {\n levels: timescaleLevels,\n unconformityLabels: _timescaleUnconformityLabels,\n }),\n h(SectionsColumn, {\n unitComponent,\n showLabels,\n width,\n columnWidth,\n showLabelColumn,\n clipUnits,\n unconformityLabels: _sectionUnconformityLabels,\n maxInternalColumns,\n }),\n children,\n ]),\n );\n}\n\ntype ColumnMouseOverHandler = (\n unit: UnitLong | null,\n height: number | null,\n evt: MouseEvent,\n) => void;\n\nfunction useMouseEventHandlers(\n _onMouseOver: ColumnMouseOverHandler | null = null,\n) {\n /** Click event handler */\n\n // Click handler for unit selection\n const dispatch = useUnitSelectionDispatch();\n const onClick = useCallback(\n (evt) => {\n dispatch?.(null, null);\n },\n [dispatch],\n );\n\n /** Hover event handlers */\n const scale = useCompositeScale();\n\n const onMouseOver = useCallback(\n (evt) => {\n const height = scale.invert(\n evt.clientY - evt.currentTarget.getBoundingClientRect().top,\n );\n _onMouseOver?.(null, height, evt);\n },\n [scale, _onMouseOver],\n );\n\n const onMouseOut = useCallback(() => {\n _onMouseOver?.(null, null, null);\n }, [_onMouseOver]);\n\n if (_onMouseOver == null) {\n return {\n onClick,\n onMouseOver: undefined,\n onMouseMove: undefined,\n onMouseOut: undefined,\n };\n }\n\n return { onMouseOver, onMouseMove: onMouseOver, onMouseOut, onClick };\n}\n\nexport interface ColumnContainerProps extends HTMLAttributes<HTMLDivElement> {\n className?: string;\n style?: CSSProperties;\n}\n\nexport function ColumnContainer(props: ColumnContainerProps) {\n const { className, ...rest } = props;\n const darkMode = useDarkMode();\n\n return h(Box, {\n className: classNames(\n className,\n {\n \"dark-mode\": darkMode?.isEnabled ?? false,\n },\n \"column-container\",\n ),\n ...rest,\n });\n}\n\nexport function ColumnBasicInfo({\n data,\n showColumnID = true,\n showReferences = true,\n}) {\n if (data == null) return null;\n return h(\"div.column-info\", [\n h(\"div.column-title-row\", [\n h(\"h2\", data.col_name),\n h.if(showColumnID)(\"h4\", h(Identifier, { id: data.col_id })),\n ]),\n h(DataField, { row: true, label: \"Group\", value: data.col_group }),\n h.if(showReferences)(ReferencesField, {\n refs: data.refs,\n inline: false,\n row: false,\n className: \"column-refs\",\n }),\n ]);\n}\n"],"names":["styles"],"mappings":"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"}
@@ -5,6 +5,9 @@ const React = require("react");
5
5
  const jotai = require("jotai");
6
6
  const store = require("./store.cjs");
7
7
  const stratigraphyUtils = require("@macrostrat/stratigraphy-utils");
8
+ function useUnitSelection() {
9
+ return store.scope.use(selectedUnitAtom);
10
+ }
8
11
  function useUnitSelectionDispatch() {
9
12
  return store.scope.useSetAtom(selectedUnitAtom);
10
13
  }
@@ -29,6 +32,7 @@ const selectedUnitAtom = jotai.atom(
29
32
  (get, set, selectedUnit, target = null) => {
30
33
  if (!get(allowUnitSelectionAtom)) {
31
34
  console.error("Unit selection is disabled.");
35
+ return null;
32
36
  }
33
37
  let unitID = null;
34
38
  let unit = null;
@@ -78,9 +82,8 @@ function UnitSelectionCallbackManager({
78
82
  return null;
79
83
  }
80
84
  function useUnitSelectionTarget(unit) {
81
- const ref = React.useRef();
82
- const selectedUnit = useSelectedUnit();
83
- const selectUnit = useUnitSelectionDispatch();
85
+ const ref = React.useRef(null);
86
+ const [selectedUnit, selectUnit] = useUnitSelection();
84
87
  const selected = selectedUnit?.unit_id == unit.unit_id;
85
88
  const onClick = React.useCallback(
86
89
  (evt) => {
@@ -220,6 +223,7 @@ exports.selectedUnitIDAtom = selectedUnitIDAtom;
220
223
  exports.useAtomOverlayPosition = useAtomOverlayPosition;
221
224
  exports.useColumnRef = useColumnRef;
222
225
  exports.useSelectedUnit = useSelectedUnit;
226
+ exports.useUnitSelection = useUnitSelection;
223
227
  exports.useUnitSelectionDispatch = useUnitSelectionDispatch;
224
228
  exports.useUnitSelectionTarget = useUnitSelectionTarget;
225
229
  //# sourceMappingURL=unit-selection.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"unit-selection.cjs","sources":["../../src/data-provider/unit-selection.ts"],"sourcesContent":["import type { BaseUnit, UnitLong } from \"@macrostrat/api-types\";\nimport { useKeyHandler } from \"@macrostrat/ui-components\";\nimport { useEffect, useRef, useCallback, useMemo } from \"react\";\nimport type { RectBounds, IUnit } from \"../units/types\";\nimport { atom } from \"jotai\";\nimport { scope, columnUnitsMapAtom } from \"./store\";\nimport {\n AgeRangeQuantifiedDifference,\n ageRangeQuantifiedDifference,\n AgeRangeRelationship,\n} from \"@macrostrat/stratigraphy-utils\";\nimport type { ColumnData } from \"@macrostrat/data-provider\";\n\ntype UnitSelectDispatch = (\n unit: number | BaseUnit | null,\n target: HTMLElement | null,\n) => void;\n\nexport function useUnitSelectionDispatch(): UnitSelectDispatch {\n return scope.useSetAtom(selectedUnitAtom);\n}\n\nexport function useSelectedUnit() {\n return scope.useAtomValue(selectedUnitAtom);\n}\n\nexport interface ColumnClickData {\n unitID: number | null;\n unit: BaseUnit | null;\n target: HTMLElement | null;\n height: number;\n // Room for boundary IDs eventually\n}\n\nexport interface UnitSelectionCallbacks {\n // It's sort of unfortunate that we need to pass in the column ref here\n onClickedColumn?: (columnClickData: ColumnClickData, event: Event) => void;\n onUnitSelected?: <T extends BaseUnit>(\n unitID: number | null,\n unit: T | null,\n ) => void;\n}\n\nexport const allowUnitSelectionAtom = atom<boolean>(true);\n\nexport const selectedUnitIDAtom = atom<number | null>();\n\nconst overlayPositionAtom = atom<RectBounds | null>();\n\nconst columnRefAtom = atom<{ current: HTMLElement | null }>({ current: null });\n\nexport function useColumnRef() {\n return scope.useAtomValue(columnRefAtom);\n}\n\nconst selectedUnitAtom = atom(\n (get) => {\n if (!get(allowUnitSelectionAtom)) return null;\n\n const unitID = get(selectedUnitIDAtom);\n if (unitID == null) return null;\n const unitsMap = get(columnUnitsMapAtom);\n return unitsMap?.get(unitID) || null;\n },\n (\n get,\n set,\n selectedUnit: number | BaseUnit | null,\n target: HTMLElement | null = null,\n ): BaseUnit | null => {\n if (!get(allowUnitSelectionAtom)) {\n console.error(\"Unit selection is disabled.\");\n }\n\n let unitID: number | null = null;\n let unit: BaseUnit | null = null;\n if (selectedUnit == null) {\n unitID = null;\n } else if (typeof selectedUnit === \"number\") {\n unitID = selectedUnit;\n } else if (\"unit_id\" in selectedUnit) {\n unitID = selectedUnit.unit_id;\n }\n\n if (unitID != null) {\n // Verify that the unit exists in the current colum, else throw\n const unitsMap = get(columnUnitsMapAtom);\n if (!unitsMap?.has(unitID)) {\n throw new Error(\n `Unit with ID ${unitID} not found in current column units.`,\n );\n }\n unit = unitsMap.get(unitID) ?? null;\n }\n\n let overlayPosition: RectBounds | null = null;\n\n const columnEl = get(columnRefAtom)?.current;\n\n if (unit != null && columnEl != null && target != null) {\n const rect = columnEl.getBoundingClientRect();\n const targetRect = target.getBoundingClientRect();\n overlayPosition = {\n x: targetRect.left - rect.left,\n y: targetRect.top - rect.top,\n width: targetRect.width,\n height: targetRect.height,\n };\n }\n\n set(selectedUnitIDAtom, unitID);\n set(overlayPositionAtom, overlayPosition);\n\n return unit;\n },\n);\n\nexport function useAtomOverlayPosition() {\n return scope.useAtomValue(overlayPositionAtom);\n}\n\nexport function UnitSelectionCallbackManager({\n onUnitSelected,\n}: UnitSelectionCallbacks) {\n const selectedUnit = scope.useAtomValue(selectedUnitAtom);\n useEffect(() => {\n onUnitSelected?.(selectedUnit?.unit_id ?? null, selectedUnit ?? null);\n }, [selectedUnit?.unit_id]);\n return null;\n}\n\n/** TODO: figure out if onClickedColumn is still needed\n /** This is not the natural place to get positions within the column,\n * but it will work for now.\nif (props.onClickedColumn) {\n // Infer height from top and bottom height of unit (because that's passed back with the call)\n //const py = event.y;\n //const [bottom, top] = getUnitHeightRange(unit, axisType);\n //const height = Math.abs(bottom - top);\n\n const columnClickData: ColumnClickData = {\n unitID: unit?.unit_id,\n unit,\n target,\n height: el?.getBoundingClientRect().height || 0,\n };\n props.onClickedColumn(columnClickData, event);\n}*/\n\nexport function useUnitSelectionTarget(\n unit: IUnit,\n): [React.RefObject<HTMLElement>, boolean, (evt: Event) => void] {\n const ref = useRef<HTMLElement>();\n const selectedUnit = useSelectedUnit();\n const selectUnit = useUnitSelectionDispatch();\n const selected = selectedUnit?.unit_id == unit.unit_id;\n\n const onClick = useCallback(\n (evt: Event) => {\n selectUnit?.(unit, ref.current);\n evt.stopPropagation();\n },\n [unit, selectUnit],\n );\n\n useEffect(() => {\n if (!selected || selectUnit == null) return;\n // In case we haven't set the position of the unit (if we don't have a target), set the selected unit\n selectUnit(unit, ref.current);\n\n // Scroll the unit into view\n ref.current?.scrollIntoView({\n behavior: \"smooth\",\n block: \"center\",\n inline: \"nearest\",\n });\n }, [selected, selectUnit]);\n\n return [ref, selected, onClick];\n}\n\nexport function UnitKeyboardNavigation({\n columnData,\n units,\n allowHorizontalNavigation,\n}: {\n columnData?: ColumnData[];\n units?: UnitLong[];\n allowHorizontalNavigation?: boolean;\n}) {\n if (units == null && columnData == null) {\n throw new Error(\"Either units or columnData must be provided.\");\n }\n\n let _columnData: ColumnData[] = useMemo(() => {\n if (columnData != null) return columnData;\n // Build column data from units\n const colMap = new Map<number, UnitLong[]>();\n for (const unit of units!) {\n if (!colMap.has(unit.col_id)) {\n colMap.set(unit.col_id, []);\n }\n colMap.get(unit.col_id)!.push(unit);\n }\n const cols: ColumnData[] = [];\n for (const [colID, units] of colMap) {\n cols.push({ columnID: colID, units });\n }\n // Sort columns by columnID\n cols.sort((a, b) => a.columnID - b.columnID);\n return cols;\n }, [columnData, units]);\n\n // Default to allowing horizontal navigation only if columnData is provided\n const _allowHorizontalNavigation =\n allowHorizontalNavigation ?? columnData != null;\n\n const selectedUnit = useSelectedUnit() as UnitLong | null;\n const selectUnit = useUnitSelectionDispatch();\n\n const keyMap: Record<number, Direction> = {\n 38: \"up\",\n 40: \"down\",\n };\n if (_allowHorizontalNavigation) {\n keyMap[37] = \"left\";\n keyMap[39] = \"right\";\n }\n\n useKeyHandler(\n (event) => {\n const direction = keyMap[event.keyCode];\n if (direction == null) return;\n const nextUnit = getBestUnit(_columnData, selectedUnit, direction);\n if (nextUnit == null) return;\n selectUnit(nextUnit, null);\n event.stopPropagation();\n },\n [_columnData, selectedUnit],\n );\n return null;\n}\n\ntype Direction = \"up\" | \"down\" | \"left\" | \"right\";\n\nfunction getBestUnit(\n columnData: ColumnData[],\n targetUnit: UnitLong,\n direction: Direction,\n): UnitLong | null {\n const thisColIndex = columnData.findIndex(\n (col) => col.columnID === targetUnit.col_id,\n );\n if (thisColIndex === -1) return null;\n\n // If up or down, stay in the same column\n if (direction === \"up\" || direction === \"down\") {\n const units = columnData[thisColIndex].units;\n const ix = units.findIndex((unit) => unit.unit_id === targetUnit.unit_id);\n if (ix === -1) return null;\n if (direction === \"up\") {\n return units[ix - 1] || null;\n } else {\n return units[ix + 1] || null;\n }\n }\n\n // If left or right, move to adjacent column\n let adjacentColIndex: number;\n if (direction === \"left\") {\n adjacentColIndex =\n (thisColIndex - 1 + columnData.length) % columnData.length;\n } else {\n adjacentColIndex = (thisColIndex + 1) % columnData.length;\n }\n\n const adjacentColUnits = columnData[adjacentColIndex].units;\n return getMostOverlappingUnit(targetUnit, adjacentColUnits);\n}\n\ntype UnitAgeRangeRelationship = AgeRangeQuantifiedDifference & {\n unit: UnitLong;\n};\n\nfunction getMostOverlappingUnit(\n targetUnit: UnitLong,\n candidateUnits: UnitLong[],\n): UnitLong | null {\n const targetAgeRange = [targetUnit.b_age, targetUnit.t_age];\n\n const overlaps: UnitAgeRangeRelationship[] = [];\n for (const candidate of candidateUnits) {\n const candidateAgeRange = [candidate.b_age, candidate.t_age];\n\n const rel = ageRangeQuantifiedDifference(targetAgeRange, candidateAgeRange);\n if (rel.type === AgeRangeRelationship.Identical) {\n return candidate;\n }\n overlaps.push({ ...rel, unit: candidate });\n }\n\n let bestOverlaps = overlaps.filter(\n (d) => d.type === AgeRangeRelationship.Containing,\n );\n bestOverlaps.sort((a, b) => b.overlap - a.overlap);\n if (bestOverlaps.length > 0) {\n return bestOverlaps[0].unit;\n }\n\n bestOverlaps = overlaps.filter(\n (d) => d.type === AgeRangeRelationship.Contained,\n );\n bestOverlaps.sort((a, b) => b.overlap - a.overlap);\n if (bestOverlaps.length > 0) {\n return bestOverlaps[0].unit;\n }\n\n bestOverlaps = overlaps.filter(\n (d) => d.type === AgeRangeRelationship.PartialOverlap,\n );\n bestOverlaps.sort((a, b) => b.overlap - a.overlap);\n if (bestOverlaps.length > 0) {\n return bestOverlaps[0].unit;\n }\n\n bestOverlaps = overlaps.filter(\n (d) => d.type === AgeRangeRelationship.Disjoint,\n );\n bestOverlaps.sort((a, b) => a.distance - b.distance);\n if (bestOverlaps.length > 0) {\n return bestOverlaps[0].unit;\n }\n return null;\n}\n"],"names":["scope","atom","columnUnitsMapAtom","useEffect","useRef","useCallback","useMemo","units","useKeyHandler","ageRangeQuantifiedDifference","AgeRangeRelationship"],"mappings":"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"}
1
+ {"version":3,"file":"unit-selection.cjs","sources":["../../src/data-provider/unit-selection.ts"],"sourcesContent":["import type { BaseUnit, UnitLong } from \"@macrostrat/api-types\";\nimport { useKeyHandler } from \"@macrostrat/ui-components\";\nimport { useEffect, useRef, useCallback, useMemo } from \"react\";\nimport type { RectBounds, IUnit } from \"../units/types\";\nimport { atom } from \"jotai\";\nimport { scope, columnUnitsMapAtom } from \"./store\";\nimport {\n AgeRangeQuantifiedDifference,\n ageRangeQuantifiedDifference,\n AgeRangeRelationship,\n} from \"@macrostrat/stratigraphy-utils\";\nimport type { ColumnData } from \"@macrostrat/data-provider\";\n\ntype UnitSelectDispatch = (\n unit: number | BaseUnit | null,\n target: HTMLElement | null,\n) => void;\n\nexport function useUnitSelection() {\n return scope.use(selectedUnitAtom);\n}\n\nexport function useUnitSelectionDispatch(): UnitSelectDispatch {\n return scope.useSetAtom(selectedUnitAtom);\n}\n\nexport function useSelectedUnit() {\n return scope.useAtomValue(selectedUnitAtom);\n}\n\nexport interface ColumnClickData {\n unitID: number | null;\n unit: BaseUnit | null;\n target: HTMLElement | null;\n height: number;\n // Room for boundary IDs eventually\n}\n\nexport interface UnitSelectionCallbacks {\n // It's sort of unfortunate that we need to pass in the column ref here\n onClickedColumn?: (columnClickData: ColumnClickData, event: Event) => void;\n onUnitSelected?: <T extends BaseUnit>(\n unitID: number | null,\n unit: T | null,\n ) => void;\n}\n\nexport const allowUnitSelectionAtom = atom<boolean>(true);\n\nexport const selectedUnitIDAtom = atom<number | null>();\n\nconst overlayPositionAtom = atom<RectBounds | null>();\n\nconst columnRefAtom = atom<{ current: HTMLElement | null }>({ current: null });\n\nexport function useColumnRef() {\n return scope.useAtomValue(columnRefAtom);\n}\n\nconst selectedUnitAtom = atom(\n (get) => {\n if (!get(allowUnitSelectionAtom)) return null;\n\n const unitID = get(selectedUnitIDAtom);\n if (unitID == null) return null;\n const unitsMap = get(columnUnitsMapAtom);\n return unitsMap?.get(unitID) || null;\n },\n (\n get,\n set,\n selectedUnit: number | BaseUnit | null,\n target: HTMLElement | null = null,\n ): BaseUnit | null => {\n if (!get(allowUnitSelectionAtom)) {\n console.error(\"Unit selection is disabled.\");\n return null;\n }\n\n let unitID: number | null = null;\n let unit: BaseUnit | null = null;\n if (selectedUnit == null) {\n unitID = null;\n } else if (typeof selectedUnit === \"number\") {\n unitID = selectedUnit;\n } else if (\"unit_id\" in selectedUnit) {\n unitID = selectedUnit.unit_id;\n }\n\n if (unitID != null) {\n const unitsMap = get(columnUnitsMapAtom);\n // Verify that the unit exists in the current colum, else throw\n if (!unitsMap?.has(unitID)) {\n throw new Error(\n `Unit with ID ${unitID} not found in current column units.`,\n );\n }\n unit = unitsMap.get(unitID) ?? null;\n }\n\n let overlayPosition: RectBounds | null = null;\n\n const columnEl = get(columnRefAtom)?.current;\n\n if (unit != null && columnEl != null && target != null) {\n const rect = columnEl.getBoundingClientRect();\n const targetRect = target.getBoundingClientRect();\n overlayPosition = {\n x: targetRect.left - rect.left,\n y: targetRect.top - rect.top,\n width: targetRect.width,\n height: targetRect.height,\n };\n }\n\n set(selectedUnitIDAtom, unitID);\n set(overlayPositionAtom, overlayPosition);\n\n return unit;\n },\n);\n\nexport function useAtomOverlayPosition() {\n return scope.useAtomValue(overlayPositionAtom);\n}\n\nexport function UnitSelectionCallbackManager({\n onUnitSelected,\n}: UnitSelectionCallbacks) {\n const selectedUnit = scope.useAtomValue(selectedUnitAtom);\n useEffect(() => {\n onUnitSelected?.(selectedUnit?.unit_id ?? null, selectedUnit ?? null);\n }, [selectedUnit?.unit_id]);\n return null;\n}\n\n/** TODO: figure out if onClickedColumn is still needed\n /** This is not the natural place to get positions within the column,\n * but it will work for now.\nif (props.onClickedColumn) {\n // Infer height from top and bottom height of unit (because that's passed back with the call)\n //const py = event.y;\n //const [bottom, top] = getUnitHeightRange(unit, axisType);\n //const height = Math.abs(bottom - top);\n\n const columnClickData: ColumnClickData = {\n unitID: unit?.unit_id,\n unit,\n target,\n height: el?.getBoundingClientRect().height || 0,\n };\n props.onClickedColumn(columnClickData, event);\n}*/\n\nexport function useUnitSelectionTarget(\n unit: IUnit,\n): [React.RefObject<HTMLElement>, boolean, (evt: Event) => void] {\n const ref = useRef<HTMLElement>(null);\n const [selectedUnit, selectUnit] = useUnitSelection();\n const selected = selectedUnit?.unit_id == unit.unit_id;\n\n const onClick = useCallback(\n (evt: Event) => {\n selectUnit?.(unit, ref.current);\n evt.stopPropagation();\n },\n [unit, selectUnit],\n );\n\n useEffect(() => {\n if (!selected || selectUnit == null) return;\n // In case we haven't set the position of the unit (if we don't have a target), set the selected unit\n selectUnit(unit, ref.current);\n\n // Scroll the unit into view\n ref.current?.scrollIntoView({\n behavior: \"smooth\",\n block: \"center\",\n inline: \"nearest\",\n });\n }, [selected, selectUnit]);\n\n return [ref, selected, onClick];\n}\n\nexport function UnitKeyboardNavigation({\n columnData,\n units,\n allowHorizontalNavigation,\n}: {\n columnData?: ColumnData[];\n units?: UnitLong[];\n allowHorizontalNavigation?: boolean;\n}) {\n if (units == null && columnData == null) {\n throw new Error(\"Either units or columnData must be provided.\");\n }\n\n let _columnData: ColumnData[] = useMemo(() => {\n if (columnData != null) return columnData;\n // Build column data from units\n const colMap = new Map<number, UnitLong[]>();\n for (const unit of units!) {\n if (!colMap.has(unit.col_id)) {\n colMap.set(unit.col_id, []);\n }\n colMap.get(unit.col_id)!.push(unit);\n }\n const cols: ColumnData[] = [];\n for (const [colID, units] of colMap) {\n cols.push({ columnID: colID, units });\n }\n // Sort columns by columnID\n cols.sort((a, b) => a.columnID - b.columnID);\n return cols;\n }, [columnData, units]);\n\n // Default to allowing horizontal navigation only if columnData is provided\n const _allowHorizontalNavigation =\n allowHorizontalNavigation ?? columnData != null;\n\n const selectedUnit = useSelectedUnit() as UnitLong | null;\n const selectUnit = useUnitSelectionDispatch();\n\n const keyMap: Record<number, Direction> = {\n 38: \"up\",\n 40: \"down\",\n };\n if (_allowHorizontalNavigation) {\n keyMap[37] = \"left\";\n keyMap[39] = \"right\";\n }\n\n useKeyHandler(\n (event) => {\n const direction = keyMap[event.keyCode];\n if (direction == null) return;\n const nextUnit = getBestUnit(_columnData, selectedUnit, direction);\n if (nextUnit == null) return;\n selectUnit(nextUnit, null);\n event.stopPropagation();\n },\n [_columnData, selectedUnit],\n );\n return null;\n}\n\ntype Direction = \"up\" | \"down\" | \"left\" | \"right\";\n\nfunction getBestUnit(\n columnData: ColumnData[],\n targetUnit: UnitLong,\n direction: Direction,\n): UnitLong | null {\n const thisColIndex = columnData.findIndex(\n (col) => col.columnID === targetUnit.col_id,\n );\n if (thisColIndex === -1) return null;\n\n // If up or down, stay in the same column\n if (direction === \"up\" || direction === \"down\") {\n const units = columnData[thisColIndex].units;\n const ix = units.findIndex((unit) => unit.unit_id === targetUnit.unit_id);\n if (ix === -1) return null;\n if (direction === \"up\") {\n return units[ix - 1] || null;\n } else {\n return units[ix + 1] || null;\n }\n }\n\n // If left or right, move to adjacent column\n let adjacentColIndex: number;\n if (direction === \"left\") {\n adjacentColIndex =\n (thisColIndex - 1 + columnData.length) % columnData.length;\n } else {\n adjacentColIndex = (thisColIndex + 1) % columnData.length;\n }\n\n const adjacentColUnits = columnData[adjacentColIndex].units;\n return getMostOverlappingUnit(targetUnit, adjacentColUnits);\n}\n\ntype UnitAgeRangeRelationship = AgeRangeQuantifiedDifference & {\n unit: UnitLong;\n};\n\nfunction getMostOverlappingUnit(\n targetUnit: UnitLong,\n candidateUnits: UnitLong[],\n): UnitLong | null {\n const targetAgeRange = [targetUnit.b_age, targetUnit.t_age];\n\n const overlaps: UnitAgeRangeRelationship[] = [];\n for (const candidate of candidateUnits) {\n const candidateAgeRange = [candidate.b_age, candidate.t_age];\n\n const rel = ageRangeQuantifiedDifference(targetAgeRange, candidateAgeRange);\n if (rel.type === AgeRangeRelationship.Identical) {\n return candidate;\n }\n overlaps.push({ ...rel, unit: candidate });\n }\n\n let bestOverlaps = overlaps.filter(\n (d) => d.type === AgeRangeRelationship.Containing,\n );\n bestOverlaps.sort((a, b) => b.overlap - a.overlap);\n if (bestOverlaps.length > 0) {\n return bestOverlaps[0].unit;\n }\n\n bestOverlaps = overlaps.filter(\n (d) => d.type === AgeRangeRelationship.Contained,\n );\n bestOverlaps.sort((a, b) => b.overlap - a.overlap);\n if (bestOverlaps.length > 0) {\n return bestOverlaps[0].unit;\n }\n\n bestOverlaps = overlaps.filter(\n (d) => d.type === AgeRangeRelationship.PartialOverlap,\n );\n bestOverlaps.sort((a, b) => b.overlap - a.overlap);\n if (bestOverlaps.length > 0) {\n return bestOverlaps[0].unit;\n }\n\n bestOverlaps = overlaps.filter(\n (d) => d.type === AgeRangeRelationship.Disjoint,\n );\n bestOverlaps.sort((a, b) => a.distance - b.distance);\n if (bestOverlaps.length > 0) {\n return bestOverlaps[0].unit;\n }\n return null;\n}\n"],"names":["scope","atom","columnUnitsMapAtom","useEffect","useRef","useCallback","useMemo","units","useKeyHandler","ageRangeQuantifiedDifference","AgeRangeRelationship"],"mappings":"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"}
@@ -2,6 +2,7 @@ import { BaseUnit, UnitLong } from '@macrostrat/api-types';
2
2
  import { IUnit } from '../units/types';
3
3
  import { ColumnData } from '@macrostrat/data-provider';
4
4
  type UnitSelectDispatch = (unit: number | BaseUnit | null, target: HTMLElement | null) => void;
5
+ export declare function useUnitSelection(): any;
5
6
  export declare function useUnitSelectionDispatch(): UnitSelectDispatch;
6
7
  export declare function useSelectedUnit(): any;
7
8
  export interface ColumnClickData {
@@ -3,6 +3,9 @@ import { useMemo, useEffect, useRef, useCallback } from "react";
3
3
  import { atom } from "jotai";
4
4
  import { scope, columnUnitsMapAtom } from "./store.js";
5
5
  import { ageRangeQuantifiedDifference, AgeRangeRelationship } from "@macrostrat/stratigraphy-utils";
6
+ function useUnitSelection() {
7
+ return scope.use(selectedUnitAtom);
8
+ }
6
9
  function useUnitSelectionDispatch() {
7
10
  return scope.useSetAtom(selectedUnitAtom);
8
11
  }
@@ -27,6 +30,7 @@ const selectedUnitAtom = atom(
27
30
  (get, set, selectedUnit, target = null) => {
28
31
  if (!get(allowUnitSelectionAtom)) {
29
32
  console.error("Unit selection is disabled.");
33
+ return null;
30
34
  }
31
35
  let unitID = null;
32
36
  let unit = null;
@@ -76,9 +80,8 @@ function UnitSelectionCallbackManager({
76
80
  return null;
77
81
  }
78
82
  function useUnitSelectionTarget(unit) {
79
- const ref = useRef();
80
- const selectedUnit = useSelectedUnit();
81
- const selectUnit = useUnitSelectionDispatch();
83
+ const ref = useRef(null);
84
+ const [selectedUnit, selectUnit] = useUnitSelection();
82
85
  const selected = selectedUnit?.unit_id == unit.unit_id;
83
86
  const onClick = useCallback(
84
87
  (evt) => {
@@ -219,6 +222,7 @@ export {
219
222
  useAtomOverlayPosition,
220
223
  useColumnRef,
221
224
  useSelectedUnit,
225
+ useUnitSelection,
222
226
  useUnitSelectionDispatch,
223
227
  useUnitSelectionTarget
224
228
  };
@@ -1 +1 @@
1
- {"version":3,"file":"unit-selection.js","sources":["../../src/data-provider/unit-selection.ts"],"sourcesContent":["import type { BaseUnit, UnitLong } from \"@macrostrat/api-types\";\nimport { useKeyHandler } from \"@macrostrat/ui-components\";\nimport { useEffect, useRef, useCallback, useMemo } from \"react\";\nimport type { RectBounds, IUnit } from \"../units/types\";\nimport { atom } from \"jotai\";\nimport { scope, columnUnitsMapAtom } from \"./store\";\nimport {\n AgeRangeQuantifiedDifference,\n ageRangeQuantifiedDifference,\n AgeRangeRelationship,\n} from \"@macrostrat/stratigraphy-utils\";\nimport type { ColumnData } from \"@macrostrat/data-provider\";\n\ntype UnitSelectDispatch = (\n unit: number | BaseUnit | null,\n target: HTMLElement | null,\n) => void;\n\nexport function useUnitSelectionDispatch(): UnitSelectDispatch {\n return scope.useSetAtom(selectedUnitAtom);\n}\n\nexport function useSelectedUnit() {\n return scope.useAtomValue(selectedUnitAtom);\n}\n\nexport interface ColumnClickData {\n unitID: number | null;\n unit: BaseUnit | null;\n target: HTMLElement | null;\n height: number;\n // Room for boundary IDs eventually\n}\n\nexport interface UnitSelectionCallbacks {\n // It's sort of unfortunate that we need to pass in the column ref here\n onClickedColumn?: (columnClickData: ColumnClickData, event: Event) => void;\n onUnitSelected?: <T extends BaseUnit>(\n unitID: number | null,\n unit: T | null,\n ) => void;\n}\n\nexport const allowUnitSelectionAtom = atom<boolean>(true);\n\nexport const selectedUnitIDAtom = atom<number | null>();\n\nconst overlayPositionAtom = atom<RectBounds | null>();\n\nconst columnRefAtom = atom<{ current: HTMLElement | null }>({ current: null });\n\nexport function useColumnRef() {\n return scope.useAtomValue(columnRefAtom);\n}\n\nconst selectedUnitAtom = atom(\n (get) => {\n if (!get(allowUnitSelectionAtom)) return null;\n\n const unitID = get(selectedUnitIDAtom);\n if (unitID == null) return null;\n const unitsMap = get(columnUnitsMapAtom);\n return unitsMap?.get(unitID) || null;\n },\n (\n get,\n set,\n selectedUnit: number | BaseUnit | null,\n target: HTMLElement | null = null,\n ): BaseUnit | null => {\n if (!get(allowUnitSelectionAtom)) {\n console.error(\"Unit selection is disabled.\");\n }\n\n let unitID: number | null = null;\n let unit: BaseUnit | null = null;\n if (selectedUnit == null) {\n unitID = null;\n } else if (typeof selectedUnit === \"number\") {\n unitID = selectedUnit;\n } else if (\"unit_id\" in selectedUnit) {\n unitID = selectedUnit.unit_id;\n }\n\n if (unitID != null) {\n // Verify that the unit exists in the current colum, else throw\n const unitsMap = get(columnUnitsMapAtom);\n if (!unitsMap?.has(unitID)) {\n throw new Error(\n `Unit with ID ${unitID} not found in current column units.`,\n );\n }\n unit = unitsMap.get(unitID) ?? null;\n }\n\n let overlayPosition: RectBounds | null = null;\n\n const columnEl = get(columnRefAtom)?.current;\n\n if (unit != null && columnEl != null && target != null) {\n const rect = columnEl.getBoundingClientRect();\n const targetRect = target.getBoundingClientRect();\n overlayPosition = {\n x: targetRect.left - rect.left,\n y: targetRect.top - rect.top,\n width: targetRect.width,\n height: targetRect.height,\n };\n }\n\n set(selectedUnitIDAtom, unitID);\n set(overlayPositionAtom, overlayPosition);\n\n return unit;\n },\n);\n\nexport function useAtomOverlayPosition() {\n return scope.useAtomValue(overlayPositionAtom);\n}\n\nexport function UnitSelectionCallbackManager({\n onUnitSelected,\n}: UnitSelectionCallbacks) {\n const selectedUnit = scope.useAtomValue(selectedUnitAtom);\n useEffect(() => {\n onUnitSelected?.(selectedUnit?.unit_id ?? null, selectedUnit ?? null);\n }, [selectedUnit?.unit_id]);\n return null;\n}\n\n/** TODO: figure out if onClickedColumn is still needed\n /** This is not the natural place to get positions within the column,\n * but it will work for now.\nif (props.onClickedColumn) {\n // Infer height from top and bottom height of unit (because that's passed back with the call)\n //const py = event.y;\n //const [bottom, top] = getUnitHeightRange(unit, axisType);\n //const height = Math.abs(bottom - top);\n\n const columnClickData: ColumnClickData = {\n unitID: unit?.unit_id,\n unit,\n target,\n height: el?.getBoundingClientRect().height || 0,\n };\n props.onClickedColumn(columnClickData, event);\n}*/\n\nexport function useUnitSelectionTarget(\n unit: IUnit,\n): [React.RefObject<HTMLElement>, boolean, (evt: Event) => void] {\n const ref = useRef<HTMLElement>();\n const selectedUnit = useSelectedUnit();\n const selectUnit = useUnitSelectionDispatch();\n const selected = selectedUnit?.unit_id == unit.unit_id;\n\n const onClick = useCallback(\n (evt: Event) => {\n selectUnit?.(unit, ref.current);\n evt.stopPropagation();\n },\n [unit, selectUnit],\n );\n\n useEffect(() => {\n if (!selected || selectUnit == null) return;\n // In case we haven't set the position of the unit (if we don't have a target), set the selected unit\n selectUnit(unit, ref.current);\n\n // Scroll the unit into view\n ref.current?.scrollIntoView({\n behavior: \"smooth\",\n block: \"center\",\n inline: \"nearest\",\n });\n }, [selected, selectUnit]);\n\n return [ref, selected, onClick];\n}\n\nexport function UnitKeyboardNavigation({\n columnData,\n units,\n allowHorizontalNavigation,\n}: {\n columnData?: ColumnData[];\n units?: UnitLong[];\n allowHorizontalNavigation?: boolean;\n}) {\n if (units == null && columnData == null) {\n throw new Error(\"Either units or columnData must be provided.\");\n }\n\n let _columnData: ColumnData[] = useMemo(() => {\n if (columnData != null) return columnData;\n // Build column data from units\n const colMap = new Map<number, UnitLong[]>();\n for (const unit of units!) {\n if (!colMap.has(unit.col_id)) {\n colMap.set(unit.col_id, []);\n }\n colMap.get(unit.col_id)!.push(unit);\n }\n const cols: ColumnData[] = [];\n for (const [colID, units] of colMap) {\n cols.push({ columnID: colID, units });\n }\n // Sort columns by columnID\n cols.sort((a, b) => a.columnID - b.columnID);\n return cols;\n }, [columnData, units]);\n\n // Default to allowing horizontal navigation only if columnData is provided\n const _allowHorizontalNavigation =\n allowHorizontalNavigation ?? columnData != null;\n\n const selectedUnit = useSelectedUnit() as UnitLong | null;\n const selectUnit = useUnitSelectionDispatch();\n\n const keyMap: Record<number, Direction> = {\n 38: \"up\",\n 40: \"down\",\n };\n if (_allowHorizontalNavigation) {\n keyMap[37] = \"left\";\n keyMap[39] = \"right\";\n }\n\n useKeyHandler(\n (event) => {\n const direction = keyMap[event.keyCode];\n if (direction == null) return;\n const nextUnit = getBestUnit(_columnData, selectedUnit, direction);\n if (nextUnit == null) return;\n selectUnit(nextUnit, null);\n event.stopPropagation();\n },\n [_columnData, selectedUnit],\n );\n return null;\n}\n\ntype Direction = \"up\" | \"down\" | \"left\" | \"right\";\n\nfunction getBestUnit(\n columnData: ColumnData[],\n targetUnit: UnitLong,\n direction: Direction,\n): UnitLong | null {\n const thisColIndex = columnData.findIndex(\n (col) => col.columnID === targetUnit.col_id,\n );\n if (thisColIndex === -1) return null;\n\n // If up or down, stay in the same column\n if (direction === \"up\" || direction === \"down\") {\n const units = columnData[thisColIndex].units;\n const ix = units.findIndex((unit) => unit.unit_id === targetUnit.unit_id);\n if (ix === -1) return null;\n if (direction === \"up\") {\n return units[ix - 1] || null;\n } else {\n return units[ix + 1] || null;\n }\n }\n\n // If left or right, move to adjacent column\n let adjacentColIndex: number;\n if (direction === \"left\") {\n adjacentColIndex =\n (thisColIndex - 1 + columnData.length) % columnData.length;\n } else {\n adjacentColIndex = (thisColIndex + 1) % columnData.length;\n }\n\n const adjacentColUnits = columnData[adjacentColIndex].units;\n return getMostOverlappingUnit(targetUnit, adjacentColUnits);\n}\n\ntype UnitAgeRangeRelationship = AgeRangeQuantifiedDifference & {\n unit: UnitLong;\n};\n\nfunction getMostOverlappingUnit(\n targetUnit: UnitLong,\n candidateUnits: UnitLong[],\n): UnitLong | null {\n const targetAgeRange = [targetUnit.b_age, targetUnit.t_age];\n\n const overlaps: UnitAgeRangeRelationship[] = [];\n for (const candidate of candidateUnits) {\n const candidateAgeRange = [candidate.b_age, candidate.t_age];\n\n const rel = ageRangeQuantifiedDifference(targetAgeRange, candidateAgeRange);\n if (rel.type === AgeRangeRelationship.Identical) {\n return candidate;\n }\n overlaps.push({ ...rel, unit: candidate });\n }\n\n let bestOverlaps = overlaps.filter(\n (d) => d.type === AgeRangeRelationship.Containing,\n );\n bestOverlaps.sort((a, b) => b.overlap - a.overlap);\n if (bestOverlaps.length > 0) {\n return bestOverlaps[0].unit;\n }\n\n bestOverlaps = overlaps.filter(\n (d) => d.type === AgeRangeRelationship.Contained,\n );\n bestOverlaps.sort((a, b) => b.overlap - a.overlap);\n if (bestOverlaps.length > 0) {\n return bestOverlaps[0].unit;\n }\n\n bestOverlaps = overlaps.filter(\n (d) => d.type === AgeRangeRelationship.PartialOverlap,\n );\n bestOverlaps.sort((a, b) => b.overlap - a.overlap);\n if (bestOverlaps.length > 0) {\n return bestOverlaps[0].unit;\n }\n\n bestOverlaps = overlaps.filter(\n (d) => d.type === AgeRangeRelationship.Disjoint,\n );\n bestOverlaps.sort((a, b) => a.distance - b.distance);\n if (bestOverlaps.length > 0) {\n return bestOverlaps[0].unit;\n }\n return null;\n}\n"],"names":["units"],"mappings":";;;;;AAkBO,SAAS,2BAA+C;AAC7D,SAAO,MAAM,WAAW,gBAAgB;AAC1C;AAEO,SAAS,kBAAkB;AAChC,SAAO,MAAM,aAAa,gBAAgB;AAC5C;AAmBO,MAAM,yBAAyB,KAAc,IAAI;AAEjD,MAAM,qBAAqB,KAAA;AAElC,MAAM,sBAAsB,KAAA;AAE5B,MAAM,gBAAgB,KAAsC,EAAE,SAAS,MAAM;AAEtE,SAAS,eAAe;AAC7B,SAAO,MAAM,aAAa,aAAa;AACzC;AAEA,MAAM,mBAAmB;AAAA,EACvB,CAAC,QAAQ;AACP,QAAI,CAAC,IAAI,sBAAsB,EAAG,QAAO;AAEzC,UAAM,SAAS,IAAI,kBAAkB;AACrC,QAAI,UAAU,KAAM,QAAO;AAC3B,UAAM,WAAW,IAAI,kBAAkB;AACvC,WAAO,UAAU,IAAI,MAAM,KAAK;AAAA,EAClC;AAAA,EACA,CACE,KACA,KACA,cACA,SAA6B,SACT;AACpB,QAAI,CAAC,IAAI,sBAAsB,GAAG;AAChC,cAAQ,MAAM,6BAA6B;AAAA,IAC7C;AAEA,QAAI,SAAwB;AAC5B,QAAI,OAAwB;AAC5B,QAAI,gBAAgB,MAAM;AACxB,eAAS;AAAA,IACX,WAAW,OAAO,iBAAiB,UAAU;AAC3C,eAAS;AAAA,IACX,WAAW,aAAa,cAAc;AACpC,eAAS,aAAa;AAAA,IACxB;AAEA,QAAI,UAAU,MAAM;AAElB,YAAM,WAAW,IAAI,kBAAkB;AACvC,UAAI,CAAC,UAAU,IAAI,MAAM,GAAG;AAC1B,cAAM,IAAI;AAAA,UACR,gBAAgB,MAAM;AAAA,QAAA;AAAA,MAE1B;AACA,aAAO,SAAS,IAAI,MAAM,KAAK;AAAA,IACjC;AAEA,QAAI,kBAAqC;AAEzC,UAAM,WAAW,IAAI,aAAa,GAAG;AAErC,QAAI,QAAQ,QAAQ,YAAY,QAAQ,UAAU,MAAM;AACtD,YAAM,OAAO,SAAS,sBAAA;AACtB,YAAM,aAAa,OAAO,sBAAA;AAC1B,wBAAkB;AAAA,QAChB,GAAG,WAAW,OAAO,KAAK;AAAA,QAC1B,GAAG,WAAW,MAAM,KAAK;AAAA,QACzB,OAAO,WAAW;AAAA,QAClB,QAAQ,WAAW;AAAA,MAAA;AAAA,IAEvB;AAEA,QAAI,oBAAoB,MAAM;AAC9B,QAAI,qBAAqB,eAAe;AAExC,WAAO;AAAA,EACT;AACF;AAEO,SAAS,yBAAyB;AACvC,SAAO,MAAM,aAAa,mBAAmB;AAC/C;AAEO,SAAS,6BAA6B;AAAA,EAC3C;AACF,GAA2B;AACzB,QAAM,eAAe,MAAM,aAAa,gBAAgB;AACxD,YAAU,MAAM;AACd,qBAAiB,cAAc,WAAW,MAAM,gBAAgB,IAAI;AAAA,EACtE,GAAG,CAAC,cAAc,OAAO,CAAC;AAC1B,SAAO;AACT;AAoBO,SAAS,uBACd,MAC+D;AAC/D,QAAM,MAAM,OAAA;AACZ,QAAM,eAAe,gBAAA;AACrB,QAAM,aAAa,yBAAA;AACnB,QAAM,WAAW,cAAc,WAAW,KAAK;AAE/C,QAAM,UAAU;AAAA,IACd,CAAC,QAAe;AACd,mBAAa,MAAM,IAAI,OAAO;AAC9B,UAAI,gBAAA;AAAA,IACN;AAAA,IACA,CAAC,MAAM,UAAU;AAAA,EAAA;AAGnB,YAAU,MAAM;AACd,QAAI,CAAC,YAAY,cAAc,KAAM;AAErC,eAAW,MAAM,IAAI,OAAO;AAG5B,QAAI,SAAS,eAAe;AAAA,MAC1B,UAAU;AAAA,MACV,OAAO;AAAA,MACP,QAAQ;AAAA,IAAA,CACT;AAAA,EACH,GAAG,CAAC,UAAU,UAAU,CAAC;AAEzB,SAAO,CAAC,KAAK,UAAU,OAAO;AAChC;AAEO,SAAS,uBAAuB;AAAA,EACrC;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,MAAI,SAAS,QAAQ,cAAc,MAAM;AACvC,UAAM,IAAI,MAAM,8CAA8C;AAAA,EAChE;AAEA,MAAI,cAA4B,QAAQ,MAAM;AAC5C,QAAI,cAAc,KAAM,QAAO;AAE/B,UAAM,6BAAa,IAAA;AACnB,eAAW,QAAQ,OAAQ;AACzB,UAAI,CAAC,OAAO,IAAI,KAAK,MAAM,GAAG;AAC5B,eAAO,IAAI,KAAK,QAAQ,CAAA,CAAE;AAAA,MAC5B;AACA,aAAO,IAAI,KAAK,MAAM,EAAG,KAAK,IAAI;AAAA,IACpC;AACA,UAAM,OAAqB,CAAA;AAC3B,eAAW,CAAC,OAAOA,MAAK,KAAK,QAAQ;AACnC,WAAK,KAAK,EAAE,UAAU,OAAO,OAAAA,QAAO;AAAA,IACtC;AAEA,SAAK,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;AAC3C,WAAO;AAAA,EACT,GAAG,CAAC,YAAY,KAAK,CAAC;AAGtB,QAAM,6BACJ,6BAA6B,cAAc;AAE7C,QAAM,eAAe,gBAAA;AACrB,QAAM,aAAa,yBAAA;AAEnB,QAAM,SAAoC;AAAA,IACxC,IAAI;AAAA,IACJ,IAAI;AAAA,EAAA;AAEN,MAAI,4BAA4B;AAC9B,WAAO,EAAE,IAAI;AACb,WAAO,EAAE,IAAI;AAAA,EACf;AAEA;AAAA,IACE,CAAC,UAAU;AACT,YAAM,YAAY,OAAO,MAAM,OAAO;AACtC,UAAI,aAAa,KAAM;AACvB,YAAM,WAAW,YAAY,aAAa,cAAc,SAAS;AACjE,UAAI,YAAY,KAAM;AACtB,iBAAW,UAAU,IAAI;AACzB,YAAM,gBAAA;AAAA,IACR;AAAA,IACA,CAAC,aAAa,YAAY;AAAA,EAAA;AAE5B,SAAO;AACT;AAIA,SAAS,YACP,YACA,YACA,WACiB;AACjB,QAAM,eAAe,WAAW;AAAA,IAC9B,CAAC,QAAQ,IAAI,aAAa,WAAW;AAAA,EAAA;AAEvC,MAAI,iBAAiB,GAAI,QAAO;AAGhC,MAAI,cAAc,QAAQ,cAAc,QAAQ;AAC9C,UAAM,QAAQ,WAAW,YAAY,EAAE;AACvC,UAAM,KAAK,MAAM,UAAU,CAAC,SAAS,KAAK,YAAY,WAAW,OAAO;AACxE,QAAI,OAAO,GAAI,QAAO;AACtB,QAAI,cAAc,MAAM;AACtB,aAAO,MAAM,KAAK,CAAC,KAAK;AAAA,IAC1B,OAAO;AACL,aAAO,MAAM,KAAK,CAAC,KAAK;AAAA,IAC1B;AAAA,EACF;AAGA,MAAI;AACJ,MAAI,cAAc,QAAQ;AACxB,wBACG,eAAe,IAAI,WAAW,UAAU,WAAW;AAAA,EACxD,OAAO;AACL,wBAAoB,eAAe,KAAK,WAAW;AAAA,EACrD;AAEA,QAAM,mBAAmB,WAAW,gBAAgB,EAAE;AACtD,SAAO,uBAAuB,YAAY,gBAAgB;AAC5D;AAMA,SAAS,uBACP,YACA,gBACiB;AACjB,QAAM,iBAAiB,CAAC,WAAW,OAAO,WAAW,KAAK;AAE1D,QAAM,WAAuC,CAAA;AAC7C,aAAW,aAAa,gBAAgB;AACtC,UAAM,oBAAoB,CAAC,UAAU,OAAO,UAAU,KAAK;AAE3D,UAAM,MAAM,6BAA6B,gBAAgB,iBAAiB;AAC1E,QAAI,IAAI,SAAS,qBAAqB,WAAW;AAC/C,aAAO;AAAA,IACT;AACA,aAAS,KAAK,EAAE,GAAG,KAAK,MAAM,WAAW;AAAA,EAC3C;AAEA,MAAI,eAAe,SAAS;AAAA,IAC1B,CAAC,MAAM,EAAE,SAAS,qBAAqB;AAAA,EAAA;AAEzC,eAAa,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO;AACjD,MAAI,aAAa,SAAS,GAAG;AAC3B,WAAO,aAAa,CAAC,EAAE;AAAA,EACzB;AAEA,iBAAe,SAAS;AAAA,IACtB,CAAC,MAAM,EAAE,SAAS,qBAAqB;AAAA,EAAA;AAEzC,eAAa,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO;AACjD,MAAI,aAAa,SAAS,GAAG;AAC3B,WAAO,aAAa,CAAC,EAAE;AAAA,EACzB;AAEA,iBAAe,SAAS;AAAA,IACtB,CAAC,MAAM,EAAE,SAAS,qBAAqB;AAAA,EAAA;AAEzC,eAAa,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO;AACjD,MAAI,aAAa,SAAS,GAAG;AAC3B,WAAO,aAAa,CAAC,EAAE;AAAA,EACzB;AAEA,iBAAe,SAAS;AAAA,IACtB,CAAC,MAAM,EAAE,SAAS,qBAAqB;AAAA,EAAA;AAEzC,eAAa,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;AACnD,MAAI,aAAa,SAAS,GAAG;AAC3B,WAAO,aAAa,CAAC,EAAE;AAAA,EACzB;AACA,SAAO;AACT;"}
1
+ {"version":3,"file":"unit-selection.js","sources":["../../src/data-provider/unit-selection.ts"],"sourcesContent":["import type { BaseUnit, UnitLong } from \"@macrostrat/api-types\";\nimport { useKeyHandler } from \"@macrostrat/ui-components\";\nimport { useEffect, useRef, useCallback, useMemo } from \"react\";\nimport type { RectBounds, IUnit } from \"../units/types\";\nimport { atom } from \"jotai\";\nimport { scope, columnUnitsMapAtom } from \"./store\";\nimport {\n AgeRangeQuantifiedDifference,\n ageRangeQuantifiedDifference,\n AgeRangeRelationship,\n} from \"@macrostrat/stratigraphy-utils\";\nimport type { ColumnData } from \"@macrostrat/data-provider\";\n\ntype UnitSelectDispatch = (\n unit: number | BaseUnit | null,\n target: HTMLElement | null,\n) => void;\n\nexport function useUnitSelection() {\n return scope.use(selectedUnitAtom);\n}\n\nexport function useUnitSelectionDispatch(): UnitSelectDispatch {\n return scope.useSetAtom(selectedUnitAtom);\n}\n\nexport function useSelectedUnit() {\n return scope.useAtomValue(selectedUnitAtom);\n}\n\nexport interface ColumnClickData {\n unitID: number | null;\n unit: BaseUnit | null;\n target: HTMLElement | null;\n height: number;\n // Room for boundary IDs eventually\n}\n\nexport interface UnitSelectionCallbacks {\n // It's sort of unfortunate that we need to pass in the column ref here\n onClickedColumn?: (columnClickData: ColumnClickData, event: Event) => void;\n onUnitSelected?: <T extends BaseUnit>(\n unitID: number | null,\n unit: T | null,\n ) => void;\n}\n\nexport const allowUnitSelectionAtom = atom<boolean>(true);\n\nexport const selectedUnitIDAtom = atom<number | null>();\n\nconst overlayPositionAtom = atom<RectBounds | null>();\n\nconst columnRefAtom = atom<{ current: HTMLElement | null }>({ current: null });\n\nexport function useColumnRef() {\n return scope.useAtomValue(columnRefAtom);\n}\n\nconst selectedUnitAtom = atom(\n (get) => {\n if (!get(allowUnitSelectionAtom)) return null;\n\n const unitID = get(selectedUnitIDAtom);\n if (unitID == null) return null;\n const unitsMap = get(columnUnitsMapAtom);\n return unitsMap?.get(unitID) || null;\n },\n (\n get,\n set,\n selectedUnit: number | BaseUnit | null,\n target: HTMLElement | null = null,\n ): BaseUnit | null => {\n if (!get(allowUnitSelectionAtom)) {\n console.error(\"Unit selection is disabled.\");\n return null;\n }\n\n let unitID: number | null = null;\n let unit: BaseUnit | null = null;\n if (selectedUnit == null) {\n unitID = null;\n } else if (typeof selectedUnit === \"number\") {\n unitID = selectedUnit;\n } else if (\"unit_id\" in selectedUnit) {\n unitID = selectedUnit.unit_id;\n }\n\n if (unitID != null) {\n const unitsMap = get(columnUnitsMapAtom);\n // Verify that the unit exists in the current colum, else throw\n if (!unitsMap?.has(unitID)) {\n throw new Error(\n `Unit with ID ${unitID} not found in current column units.`,\n );\n }\n unit = unitsMap.get(unitID) ?? null;\n }\n\n let overlayPosition: RectBounds | null = null;\n\n const columnEl = get(columnRefAtom)?.current;\n\n if (unit != null && columnEl != null && target != null) {\n const rect = columnEl.getBoundingClientRect();\n const targetRect = target.getBoundingClientRect();\n overlayPosition = {\n x: targetRect.left - rect.left,\n y: targetRect.top - rect.top,\n width: targetRect.width,\n height: targetRect.height,\n };\n }\n\n set(selectedUnitIDAtom, unitID);\n set(overlayPositionAtom, overlayPosition);\n\n return unit;\n },\n);\n\nexport function useAtomOverlayPosition() {\n return scope.useAtomValue(overlayPositionAtom);\n}\n\nexport function UnitSelectionCallbackManager({\n onUnitSelected,\n}: UnitSelectionCallbacks) {\n const selectedUnit = scope.useAtomValue(selectedUnitAtom);\n useEffect(() => {\n onUnitSelected?.(selectedUnit?.unit_id ?? null, selectedUnit ?? null);\n }, [selectedUnit?.unit_id]);\n return null;\n}\n\n/** TODO: figure out if onClickedColumn is still needed\n /** This is not the natural place to get positions within the column,\n * but it will work for now.\nif (props.onClickedColumn) {\n // Infer height from top and bottom height of unit (because that's passed back with the call)\n //const py = event.y;\n //const [bottom, top] = getUnitHeightRange(unit, axisType);\n //const height = Math.abs(bottom - top);\n\n const columnClickData: ColumnClickData = {\n unitID: unit?.unit_id,\n unit,\n target,\n height: el?.getBoundingClientRect().height || 0,\n };\n props.onClickedColumn(columnClickData, event);\n}*/\n\nexport function useUnitSelectionTarget(\n unit: IUnit,\n): [React.RefObject<HTMLElement>, boolean, (evt: Event) => void] {\n const ref = useRef<HTMLElement>(null);\n const [selectedUnit, selectUnit] = useUnitSelection();\n const selected = selectedUnit?.unit_id == unit.unit_id;\n\n const onClick = useCallback(\n (evt: Event) => {\n selectUnit?.(unit, ref.current);\n evt.stopPropagation();\n },\n [unit, selectUnit],\n );\n\n useEffect(() => {\n if (!selected || selectUnit == null) return;\n // In case we haven't set the position of the unit (if we don't have a target), set the selected unit\n selectUnit(unit, ref.current);\n\n // Scroll the unit into view\n ref.current?.scrollIntoView({\n behavior: \"smooth\",\n block: \"center\",\n inline: \"nearest\",\n });\n }, [selected, selectUnit]);\n\n return [ref, selected, onClick];\n}\n\nexport function UnitKeyboardNavigation({\n columnData,\n units,\n allowHorizontalNavigation,\n}: {\n columnData?: ColumnData[];\n units?: UnitLong[];\n allowHorizontalNavigation?: boolean;\n}) {\n if (units == null && columnData == null) {\n throw new Error(\"Either units or columnData must be provided.\");\n }\n\n let _columnData: ColumnData[] = useMemo(() => {\n if (columnData != null) return columnData;\n // Build column data from units\n const colMap = new Map<number, UnitLong[]>();\n for (const unit of units!) {\n if (!colMap.has(unit.col_id)) {\n colMap.set(unit.col_id, []);\n }\n colMap.get(unit.col_id)!.push(unit);\n }\n const cols: ColumnData[] = [];\n for (const [colID, units] of colMap) {\n cols.push({ columnID: colID, units });\n }\n // Sort columns by columnID\n cols.sort((a, b) => a.columnID - b.columnID);\n return cols;\n }, [columnData, units]);\n\n // Default to allowing horizontal navigation only if columnData is provided\n const _allowHorizontalNavigation =\n allowHorizontalNavigation ?? columnData != null;\n\n const selectedUnit = useSelectedUnit() as UnitLong | null;\n const selectUnit = useUnitSelectionDispatch();\n\n const keyMap: Record<number, Direction> = {\n 38: \"up\",\n 40: \"down\",\n };\n if (_allowHorizontalNavigation) {\n keyMap[37] = \"left\";\n keyMap[39] = \"right\";\n }\n\n useKeyHandler(\n (event) => {\n const direction = keyMap[event.keyCode];\n if (direction == null) return;\n const nextUnit = getBestUnit(_columnData, selectedUnit, direction);\n if (nextUnit == null) return;\n selectUnit(nextUnit, null);\n event.stopPropagation();\n },\n [_columnData, selectedUnit],\n );\n return null;\n}\n\ntype Direction = \"up\" | \"down\" | \"left\" | \"right\";\n\nfunction getBestUnit(\n columnData: ColumnData[],\n targetUnit: UnitLong,\n direction: Direction,\n): UnitLong | null {\n const thisColIndex = columnData.findIndex(\n (col) => col.columnID === targetUnit.col_id,\n );\n if (thisColIndex === -1) return null;\n\n // If up or down, stay in the same column\n if (direction === \"up\" || direction === \"down\") {\n const units = columnData[thisColIndex].units;\n const ix = units.findIndex((unit) => unit.unit_id === targetUnit.unit_id);\n if (ix === -1) return null;\n if (direction === \"up\") {\n return units[ix - 1] || null;\n } else {\n return units[ix + 1] || null;\n }\n }\n\n // If left or right, move to adjacent column\n let adjacentColIndex: number;\n if (direction === \"left\") {\n adjacentColIndex =\n (thisColIndex - 1 + columnData.length) % columnData.length;\n } else {\n adjacentColIndex = (thisColIndex + 1) % columnData.length;\n }\n\n const adjacentColUnits = columnData[adjacentColIndex].units;\n return getMostOverlappingUnit(targetUnit, adjacentColUnits);\n}\n\ntype UnitAgeRangeRelationship = AgeRangeQuantifiedDifference & {\n unit: UnitLong;\n};\n\nfunction getMostOverlappingUnit(\n targetUnit: UnitLong,\n candidateUnits: UnitLong[],\n): UnitLong | null {\n const targetAgeRange = [targetUnit.b_age, targetUnit.t_age];\n\n const overlaps: UnitAgeRangeRelationship[] = [];\n for (const candidate of candidateUnits) {\n const candidateAgeRange = [candidate.b_age, candidate.t_age];\n\n const rel = ageRangeQuantifiedDifference(targetAgeRange, candidateAgeRange);\n if (rel.type === AgeRangeRelationship.Identical) {\n return candidate;\n }\n overlaps.push({ ...rel, unit: candidate });\n }\n\n let bestOverlaps = overlaps.filter(\n (d) => d.type === AgeRangeRelationship.Containing,\n );\n bestOverlaps.sort((a, b) => b.overlap - a.overlap);\n if (bestOverlaps.length > 0) {\n return bestOverlaps[0].unit;\n }\n\n bestOverlaps = overlaps.filter(\n (d) => d.type === AgeRangeRelationship.Contained,\n );\n bestOverlaps.sort((a, b) => b.overlap - a.overlap);\n if (bestOverlaps.length > 0) {\n return bestOverlaps[0].unit;\n }\n\n bestOverlaps = overlaps.filter(\n (d) => d.type === AgeRangeRelationship.PartialOverlap,\n );\n bestOverlaps.sort((a, b) => b.overlap - a.overlap);\n if (bestOverlaps.length > 0) {\n return bestOverlaps[0].unit;\n }\n\n bestOverlaps = overlaps.filter(\n (d) => d.type === AgeRangeRelationship.Disjoint,\n );\n bestOverlaps.sort((a, b) => a.distance - b.distance);\n if (bestOverlaps.length > 0) {\n return bestOverlaps[0].unit;\n }\n return null;\n}\n"],"names":["units"],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -10,7 +10,7 @@ const ageRange = require("./unit-details/age-range.cjs");
10
10
  const composite = require("./units/composite.cjs");
11
11
  const columns = require("./maps/layers/columns.cjs");
12
12
  const base = require("./facets/measurements/base.cjs");
13
- const panel = require("./unit-details/panel.cjs");
13
+ const identifiers = require("./unit-details/identifiers.cjs");
14
14
  const layers = require("./maps/column-navigation/svg/layers.cjs");
15
15
  const core = require("./maps/column-correlation/core.cjs");
16
16
  const state = require("./maps/column-correlation/state.cjs");
@@ -23,6 +23,7 @@ const provider = require("./facets/fossils/provider.cjs");
23
23
  const types = require("./prepare-units/types.cjs");
24
24
  const index$4 = require("./maps/inset-map/index.cjs");
25
25
  const isotopesColumn = require("./facets/carbon-isotopes/isotopes-column.cjs");
26
+ const panel = require("./unit-details/panel.cjs");
26
27
  const store = require("./data-provider/store.cjs");
27
28
  const units = require("./maps/layers/units.cjs");
28
29
  const dataProvider = require("./facets/carbon-isotopes/data-provider.cjs");
@@ -30,6 +31,7 @@ const utils = require("./prepare-units/utils.cjs");
30
31
  const index$5 = require("./facets/fossils/index.cjs");
31
32
  const taxonRanges = require("./facets/fossils/taxon-ranges.cjs");
32
33
  const sgp = require("./facets/measurements/sgp.cjs");
34
+ const stratNames = require("./unit-details/strat-names.cjs");
33
35
  const modalPanel = require("./unit-details/modal-panel.cjs");
34
36
  const popover = require("./unit-details/popover.cjs");
35
37
  const unitSelection = require("./data-provider/unit-selection.cjs");
@@ -79,13 +81,9 @@ exports.TruncatedList = base.TruncatedList;
79
81
  exports.groupNotesByPixelDistance = base.groupNotesByPixelDistance;
80
82
  exports.mergeHeightRanges = base.mergeHeightRanges;
81
83
  exports.standardizeMeasurementHeight = base.standardizeMeasurementHeight;
82
- exports.ClickableText = panel.ClickableText;
83
- exports.Identifier = panel.Identifier;
84
- exports.LegendPanelHeader = panel.LegendPanelHeader;
85
- exports.ReferencesField = panel.ReferencesField;
86
- exports.ThicknessField = panel.ThicknessField;
87
- exports.UnitDetailsFeature = panel.UnitDetailsFeature;
88
- exports.UnitDetailsPanel = panel.UnitDetailsPanel;
84
+ exports.ClickableText = identifiers.ClickableText;
85
+ exports.Identifier = identifiers.Identifier;
86
+ exports.UnitIdentifier = identifiers.UnitIdentifier;
89
87
  exports.ColumnCenters = layers.ColumnCenters;
90
88
  exports.ColumnFeatures = layers.ColumnFeatures;
91
89
  exports.ColumnKeyboardNavigation = layers.ColumnKeyboardNavigation;
@@ -113,6 +111,11 @@ exports.HybridScaleType = types.HybridScaleType;
113
111
  exports.InsetMap = index$4.InsetMap;
114
112
  exports.IsotopesColumn = isotopesColumn.IsotopesColumn;
115
113
  exports.IsotopesDataset = isotopesColumn.IsotopesDataset;
114
+ exports.LegendPanelHeader = panel.LegendPanelHeader;
115
+ exports.ReferencesField = panel.ReferencesField;
116
+ exports.ThicknessField = panel.ThicknessField;
117
+ exports.UnitDetailsFeature = panel.UnitDetailsFeature;
118
+ exports.UnitDetailsPanel = panel.UnitDetailsPanel;
116
119
  exports.LithologiesProvider = store.LithologiesProvider;
117
120
  exports.MacrostratColumnDataProvider = store.MacrostratColumnDataProvider;
118
121
  exports.MacrostratColumnProvider = store.MacrostratColumnProvider;
@@ -141,6 +144,7 @@ exports.unitsOverlap = utils.unitsOverlap;
141
144
  exports.PBDBFossilsColumn = index$5.PBDBFossilsColumn;
142
145
  exports.PBDBOccurrencesMatrix = taxonRanges.PBDBOccurrencesMatrix;
143
146
  exports.SGPMeasurementsColumn = sgp.SGPMeasurementsColumn;
147
+ exports.StratNameField = stratNames.StratNameField;
144
148
  exports.UnitDetailsPanelWithNavigation = modalPanel.UnitDetailsPanelWithNavigation;
145
149
  exports.UnitDetailsPopover = popover.UnitDetailsPopover;
146
150
  exports.UnitSelectionPopover = popover.UnitSelectionPopover;
@@ -151,6 +155,7 @@ exports.selectedUnitIDAtom = unitSelection.selectedUnitIDAtom;
151
155
  exports.useAtomOverlayPosition = unitSelection.useAtomOverlayPosition;
152
156
  exports.useColumnRef = unitSelection.useColumnRef;
153
157
  exports.useSelectedUnit = unitSelection.useSelectedUnit;
158
+ exports.useUnitSelection = unitSelection.useUnitSelection;
154
159
  exports.useUnitSelectionDispatch = unitSelection.useUnitSelectionDispatch;
155
160
  exports.useUnitSelectionTarget = unitSelection.useUnitSelectionTarget;
156
161
  exports.flattenLithologies = colors.flattenLithologies;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import { AgeField, AgeLabel, AgeRange, Duration, IntervalProportions, getAge, ge
8
8
  import { AnnotatedUnitsColumn, ColumnNotesProvider, CompositeUnitComponent, CompositeUnitsColumn, LabelTrackerProvider, SectionLabelsColumn, TrackedLabeledUnit } from "./units/composite.js";
9
9
  import { BaseColumnsLayer, SelectedColumnOverlay, buildColumnsStyle } from "./maps/layers/columns.js";
10
10
  import { BaseMeasurementsColumn, TruncatedList, groupNotesByPixelDistance, mergeHeightRanges, standardizeMeasurementHeight } from "./facets/measurements/base.js";
11
- import { ClickableText, Identifier, LegendPanelHeader, ReferencesField, ThicknessField, UnitDetailsFeature, UnitDetailsPanel } from "./unit-details/panel.js";
11
+ import { ClickableText, Identifier, UnitIdentifier } from "./unit-details/identifiers.js";
12
12
  import { ColumnCenters, ColumnFeatures, ColumnKeyboardNavigation, Columns, CurrentColumn, MeasurementsLayer } from "./maps/column-navigation/svg/layers.js";
13
13
  import { ColumnCorrelationMap } from "./maps/column-correlation/core.js";
14
14
  import { ColumnCorrelationProvider, useCorrelationMapStore } from "./maps/column-correlation/state.js";
@@ -21,6 +21,7 @@ import { FossilDataType, useFossilData } from "./facets/fossils/provider.js";
21
21
  import { HeightMethod, HybridScaleType } from "./prepare-units/types.js";
22
22
  import { InsetMap } from "./maps/inset-map/index.js";
23
23
  import { IsotopesColumn, IsotopesDataset } from "./facets/carbon-isotopes/isotopes-column.js";
24
+ import { LegendPanelHeader, ReferencesField, ThicknessField, UnitDetailsFeature, UnitDetailsPanel } from "./unit-details/panel.js";
24
25
  import { LithologiesProvider, MacrostratColumnDataProvider, MacrostratColumnProvider, MacrostratColumnStateProvider, UnitSelectionProvider, columnUnitsAtom, columnUnitsMapAtom, scope, useColumnUnitsMap, useCompositeScale, useMacrostratColumnData, useMacrostratUnits } from "./data-provider/store.js";
25
26
  import { MacrostratUnitsOverlay, buildUnitsStyle } from "./maps/layers/units.js";
26
27
  import { MeasurementDataContext, MeasurementDataProvider, useMeasurementData } from "./facets/carbon-isotopes/data-provider.js";
@@ -28,9 +29,10 @@ import { MergeSectionsMode, agesOverlap, createUnitSorter, ensureArray, ensureRe
28
29
  import { PBDBFossilsColumn } from "./facets/fossils/index.js";
29
30
  import { PBDBOccurrencesMatrix } from "./facets/fossils/taxon-ranges.js";
30
31
  import { SGPMeasurementsColumn } from "./facets/measurements/sgp.js";
32
+ import { StratNameField } from "./unit-details/strat-names.js";
31
33
  import { UnitDetailsPanelWithNavigation } from "./unit-details/modal-panel.js";
32
34
  import { UnitDetailsPopover, UnitSelectionPopover } from "./unit-details/popover.js";
33
- import { UnitKeyboardNavigation, UnitSelectionCallbackManager, allowUnitSelectionAtom, selectedUnitIDAtom, useAtomOverlayPosition, useColumnRef, useSelectedUnit, useUnitSelectionDispatch, useUnitSelectionTarget } from "./data-provider/unit-selection.js";
35
+ import { UnitKeyboardNavigation, UnitSelectionCallbackManager, allowUnitSelectionAtom, selectedUnitIDAtom, useAtomOverlayPosition, useColumnRef, useSelectedUnit, useUnitSelection, useUnitSelectionDispatch, useUnitSelectionTarget } from "./data-provider/unit-selection.js";
34
36
  import { flattenLithologies, getMixedColorForData, getMixedUnitColor } from "./units/colors.js";
35
37
  import { parseLineFromString, stringifyLine } from "./correlation-chart/hash-string.js";
36
38
  import { preprocessUnits } from "./prepare-units/helpers.js";
@@ -102,6 +104,7 @@ export {
102
104
  SectionLabelsColumn,
103
105
  SelectedColumnOverlay,
104
106
  SimpleUnitsColumn,
107
+ StratNameField,
105
108
  ThicknessField,
106
109
  TrackedLabeledUnit,
107
110
  TruncatedList,
@@ -110,6 +113,7 @@ export {
110
113
  UnitDetailsPanel,
111
114
  UnitDetailsPanelWithNavigation,
112
115
  UnitDetailsPopover,
116
+ UnitIdentifier,
113
117
  UnitKeyboardNavigation,
114
118
  UnitSelectionCallbackManager,
115
119
  UnitSelectionPopover,
@@ -156,6 +160,7 @@ export {
156
160
  useSelectedUnit,
157
161
  useUnitColor,
158
162
  useUnitColorByEnvironment,
163
+ useUnitSelection,
159
164
  useUnitSelectionDispatch,
160
165
  useUnitSelectionTarget
161
166
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}