@expressivecss/expressive 0.12.0 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +339 -1
- package/README.md +38 -1
- package/dist/css/expressive.css +5342 -105
- package/dist/css/expressive.css.map +1 -1
- package/dist/css/expressive.min.css +1 -1
- package/dist/css/expressive.min.css.map +1 -1
- package/dist/fonts/material-symbols-outlined.woff2 +0 -0
- package/dist/fonts/material-symbols-rounded.woff2 +0 -0
- package/dist/fonts/material-symbols-sharp.woff2 +0 -0
- package/dist/js/chunks/chunk-3DHK5CZU.mjs +105 -0
- package/dist/js/chunks/chunk-3DHK5CZU.mjs.map +7 -0
- package/dist/js/chunks/{chunk-HB6IW7QQ.mjs → chunk-3GVFZ4XO.mjs} +3 -3
- package/dist/js/chunks/{chunk-HLDXZ7XB.mjs → chunk-4HZMB7LI.mjs} +22 -8
- package/dist/js/chunks/chunk-4HZMB7LI.mjs.map +7 -0
- package/dist/js/chunks/chunk-4KMNFIFZ.mjs +270 -0
- package/dist/js/chunks/chunk-4KMNFIFZ.mjs.map +7 -0
- package/dist/js/chunks/chunk-4TKTLGEM.mjs +262 -0
- package/dist/js/chunks/chunk-4TKTLGEM.mjs.map +7 -0
- package/dist/js/chunks/{chunk-3VOK6J7U.mjs → chunk-5BKHWHYY.mjs} +13 -6
- package/dist/js/chunks/chunk-5BKHWHYY.mjs.map +7 -0
- package/dist/js/chunks/{chunk-H2WA4XHH.mjs → chunk-5HQSGDZ5.mjs} +16 -7
- package/dist/js/chunks/chunk-5HQSGDZ5.mjs.map +7 -0
- package/dist/js/chunks/{chunk-LWBQIX5J.mjs → chunk-A7ALMYUR.mjs} +41 -44
- package/dist/js/chunks/chunk-A7ALMYUR.mjs.map +7 -0
- package/dist/js/chunks/chunk-BG6ZRB5H.mjs +154 -0
- package/dist/js/chunks/chunk-BG6ZRB5H.mjs.map +7 -0
- package/dist/js/chunks/{chunk-DLWYKFEI.mjs → chunk-CUQPB25U.mjs} +3 -3
- package/dist/js/chunks/{chunk-6WLSDZ4O.mjs → chunk-DAIU5SLG.mjs} +2 -2
- package/dist/js/chunks/chunk-FS7XY3HT.mjs +124 -0
- package/dist/js/chunks/chunk-FS7XY3HT.mjs.map +7 -0
- package/dist/js/chunks/{chunk-WJXDBPXQ.mjs → chunk-GIW4K4SE.mjs} +8 -12
- package/dist/js/chunks/chunk-GIW4K4SE.mjs.map +7 -0
- package/dist/js/chunks/chunk-GUB3TNO2.mjs +164 -0
- package/dist/js/chunks/chunk-GUB3TNO2.mjs.map +7 -0
- package/dist/js/chunks/chunk-GVGT6MIU.mjs +47 -0
- package/dist/js/chunks/chunk-GVGT6MIU.mjs.map +7 -0
- package/dist/js/chunks/{chunk-HNFSMIBY.mjs → chunk-GZKEFWPM.mjs} +83 -22
- package/dist/js/chunks/chunk-GZKEFWPM.mjs.map +7 -0
- package/dist/js/chunks/chunk-HKT2S7VM.mjs +48 -0
- package/dist/js/chunks/chunk-HKT2S7VM.mjs.map +7 -0
- package/dist/js/chunks/chunk-ILP3PDTL.mjs +187 -0
- package/dist/js/chunks/chunk-ILP3PDTL.mjs.map +7 -0
- package/dist/js/chunks/{chunk-YSBCHYXP.mjs → chunk-JIDMQGK6.mjs} +118 -13
- package/dist/js/chunks/chunk-JIDMQGK6.mjs.map +7 -0
- package/dist/js/chunks/chunk-KBNN3QDH.mjs +7 -0
- package/dist/js/chunks/chunk-KBNN3QDH.mjs.map +7 -0
- package/dist/js/chunks/{chunk-NNYP33NB.mjs → chunk-KHV6DZXX.mjs} +5 -4
- package/dist/js/chunks/chunk-KHV6DZXX.mjs.map +7 -0
- package/dist/js/chunks/{chunk-RFZZCYVU.mjs → chunk-KY6U46SM.mjs} +3 -3
- package/dist/js/chunks/{chunk-CKQP6P76.mjs → chunk-LRZMJZ3T.mjs} +4 -3
- package/dist/js/chunks/chunk-LRZMJZ3T.mjs.map +7 -0
- package/dist/js/chunks/{chunk-6OMRGML6.mjs → chunk-MNPROCZF.mjs} +4 -4
- package/dist/js/chunks/{chunk-6OMRGML6.mjs.map → chunk-MNPROCZF.mjs.map} +2 -2
- package/dist/js/chunks/{chunk-QV2JAWIZ.mjs → chunk-MZ6Z24BX.mjs} +3 -2
- package/dist/js/chunks/{chunk-QV2JAWIZ.mjs.map → chunk-MZ6Z24BX.mjs.map} +2 -2
- package/dist/js/chunks/chunk-NS2UMNZV.mjs +292 -0
- package/dist/js/chunks/chunk-NS2UMNZV.mjs.map +7 -0
- package/dist/js/chunks/chunk-O5XPWXIQ.mjs +406 -0
- package/dist/js/chunks/chunk-O5XPWXIQ.mjs.map +7 -0
- package/dist/js/chunks/{chunk-UBLRRJ7N.mjs → chunk-PM655FXL.mjs} +2 -2
- package/dist/js/chunks/{chunk-IFXVDDDH.mjs → chunk-QERLECVU.mjs} +3 -3
- package/dist/js/chunks/{chunk-GZWPXXY6.mjs → chunk-QJABDLSD.mjs} +5 -2
- package/dist/js/chunks/chunk-QJABDLSD.mjs.map +7 -0
- package/dist/js/chunks/chunk-RHRQKTTX.mjs +139 -0
- package/dist/js/chunks/chunk-RHRQKTTX.mjs.map +7 -0
- package/dist/js/chunks/{chunk-UNEMYWEW.mjs → chunk-S3XDFGSZ.mjs} +2 -2
- package/dist/js/chunks/chunk-SPETGK25.mjs +59 -0
- package/dist/js/chunks/chunk-SPETGK25.mjs.map +7 -0
- package/dist/js/chunks/chunk-TBM3FT7G.mjs +398 -0
- package/dist/js/chunks/chunk-TBM3FT7G.mjs.map +7 -0
- package/dist/js/chunks/chunk-UEXCIMJZ.mjs +214 -0
- package/dist/js/chunks/chunk-UEXCIMJZ.mjs.map +7 -0
- package/dist/js/chunks/chunk-VGZSAFTX.mjs +173 -0
- package/dist/js/chunks/chunk-VGZSAFTX.mjs.map +7 -0
- package/dist/js/chunks/{chunk-UAEAQBRC.mjs → chunk-X7OCHZVF.mjs} +2 -2
- package/dist/js/chunks/{chunk-EC63CHXU.mjs → chunk-YMSXJON6.mjs} +7 -6
- package/dist/js/chunks/chunk-YMSXJON6.mjs.map +7 -0
- package/dist/js/chunks/{chunk-QVVEWBOF.mjs → chunk-Z2NRKI7R.mjs} +2 -2
- package/dist/js/chunks/{chunk-PIJ2TAR3.mjs → chunk-ZAS25YFD.mjs} +2 -2
- package/dist/js/chunks/chunk-ZKZKNSLC.mjs +1 -0
- package/dist/js/expressive.cjs +3113 -148
- package/dist/js/expressive.cjs.map +4 -4
- package/dist/js/expressive.js +3113 -148
- package/dist/js/expressive.js.map +4 -4
- package/dist/js/expressive.min.js +11 -11
- package/dist/js/expressive.min.js.map +4 -4
- package/dist/js/expressive.mjs +95 -35
- package/dist/js/expressive.mjs.map +1 -1
- package/dist/js/internal/behaviors/bottomSheets.mjs +3 -3
- package/dist/js/internal/behaviors/dialogs.mjs +2 -2
- package/dist/js/internal/behaviors/forms.mjs +2 -2
- package/dist/js/internal/behaviors/sheetDrag.mjs +2 -2
- package/dist/js/internal/behaviors/sideSheets.mjs +3 -3
- package/dist/js/internal/components/autocomplete.mjs +3 -3
- package/dist/js/internal/components/barChart.mjs +10 -0
- package/dist/js/internal/components/barChart.mjs.map +7 -0
- package/dist/js/internal/components/buttons.mjs +2 -2
- package/dist/js/internal/components/cards.mjs +2 -2
- package/dist/js/internal/components/carousel.mjs +2 -2
- package/dist/js/internal/components/chips.mjs +4 -4
- package/dist/js/internal/components/columnChart.mjs +9 -0
- package/dist/js/internal/components/columnChart.mjs.map +7 -0
- package/dist/js/internal/components/commandPalette.mjs +9 -0
- package/dist/js/internal/components/commandPalette.mjs.map +7 -0
- package/dist/js/internal/components/confetti.mjs +8 -0
- package/dist/js/internal/components/confetti.mjs.map +7 -0
- package/dist/js/internal/components/datepicker.mjs +5 -5
- package/dist/js/internal/components/expandingCard.mjs +2 -2
- package/dist/js/internal/components/heatmapChart.mjs +9 -0
- package/dist/js/internal/components/heatmapChart.mjs.map +7 -0
- package/dist/js/internal/components/index.mjs +88 -28
- package/dist/js/internal/components/lightbox.mjs +2 -2
- package/dist/js/internal/components/lineChart.mjs +12 -0
- package/dist/js/internal/components/lineChart.mjs.map +7 -0
- package/dist/js/internal/components/mapView.mjs +9 -0
- package/dist/js/internal/components/mapView.mjs.map +7 -0
- package/dist/js/internal/components/menu.mjs +2 -2
- package/dist/js/internal/components/messageScroller.mjs +9 -0
- package/dist/js/internal/components/messageScroller.mjs.map +7 -0
- package/dist/js/internal/components/mixedChart.mjs +10 -0
- package/dist/js/internal/components/mixedChart.mjs.map +7 -0
- package/dist/js/internal/components/particles.mjs +8 -0
- package/dist/js/internal/components/particles.mjs.map +7 -0
- package/dist/js/internal/components/pieChart.mjs +9 -0
- package/dist/js/internal/components/pieChart.mjs.map +7 -0
- package/dist/js/internal/components/questionnaire.mjs +9 -0
- package/dist/js/internal/components/questionnaire.mjs.map +7 -0
- package/dist/js/internal/components/radarChart.mjs +9 -0
- package/dist/js/internal/components/radarChart.mjs.map +7 -0
- package/dist/js/internal/components/radialChart.mjs +9 -0
- package/dist/js/internal/components/radialChart.mjs.map +7 -0
- package/dist/js/internal/components/registry.mjs +35 -20
- package/dist/js/internal/components/richTextEditor.mjs +9 -0
- package/dist/js/internal/components/richTextEditor.mjs.map +7 -0
- package/dist/js/internal/components/scrollspy.mjs +1 -1
- package/dist/js/internal/components/select.mjs +3 -3
- package/dist/js/internal/components/slider.mjs +2 -2
- package/dist/js/internal/components/snackbar.mjs +2 -2
- package/dist/js/internal/components/tabs.mjs +3 -3
- package/dist/js/internal/components/timepicker.mjs +3 -3
- package/dist/js/internal/components/tooltip.mjs +2 -2
- package/dist/js/internal/core/utils.mjs +1 -1
- package/dist/js/internal/plugins/dockedDisplayPlugin.mjs +2 -2
- package/dist/js/modular.mjs +95 -35
- package/dist/types/behaviors/forms.d.ts +27 -0
- package/dist/types/components/barChart.d.ts +24 -0
- package/dist/types/components/carousel.d.ts +2 -0
- package/dist/types/components/columnChart.d.ts +21 -0
- package/dist/types/components/commandPalette.d.ts +96 -0
- package/dist/types/components/confetti.d.ts +103 -0
- package/dist/types/components/datepicker.d.ts +2 -0
- package/dist/types/components/heatmapChart.d.ts +49 -0
- package/dist/types/components/index.d.ts +15 -0
- package/dist/types/components/lineChart.d.ts +134 -0
- package/dist/types/components/mapView.d.ts +151 -0
- package/dist/types/components/menu.d.ts +31 -0
- package/dist/types/components/messageScroller.d.ts +111 -0
- package/dist/types/components/mixedChart.d.ts +25 -0
- package/dist/types/components/particles.d.ts +72 -0
- package/dist/types/components/pieChart.d.ts +43 -0
- package/dist/types/components/questionnaire.d.ts +96 -0
- package/dist/types/components/radarChart.d.ts +39 -0
- package/dist/types/components/radialChart.d.ts +45 -0
- package/dist/types/components/registry.d.ts +52 -0
- package/dist/types/components/richTextEditor.d.ts +71 -0
- package/dist/types/components/slider.d.ts +1 -1
- package/dist/types/components/snackbar.d.ts +2 -0
- package/dist/types/components/tabs.d.ts +1 -1
- package/dist/types/modular.d.ts +10 -1
- package/dist/types/plugins/dockedDisplayPlugin.d.ts +3 -3
- package/package.json +5 -1
- package/src/sass/README.md +3 -6
- package/src/sass/abstracts/_index.scss +1 -1
- package/src/sass/abstracts/_mixins.scss +226 -0
- package/src/sass/abstracts/_variables.scss +14 -0
- package/src/sass/base/_typography.scss +45 -0
- package/src/sass/components/_accordion.scss +161 -0
- package/src/sass/components/_aura.scss +119 -0
- package/src/sass/components/_avatar.scss +86 -0
- package/src/sass/components/_bar-chart.scss +285 -0
- package/src/sass/components/_bottom-sheet.scss +1 -0
- package/src/sass/components/_button-groups.scss +13 -3
- package/src/sass/components/_buttons.scss +1 -1
- package/src/sass/components/_cards.scss +16 -13
- package/src/sass/components/_column-chart.scss +134 -0
- package/src/sass/components/_command-palette.scss +140 -0
- package/src/sass/components/_confetti.scss +14 -0
- package/src/sass/components/_countdown.scss +89 -0
- package/src/sass/components/_data-table.scss +188 -0
- package/src/sass/components/_dialog.scss +1 -0
- package/src/sass/components/_empty-state.scss +78 -0
- package/src/sass/components/_expanding-card.scss +3 -0
- package/src/sass/components/_heatmap-chart.scss +156 -0
- package/src/sass/components/_index.scss +30 -0
- package/src/sass/components/_kpi.scss +162 -0
- package/src/sass/components/_line-chart.scss +102 -0
- package/src/sass/components/_map.scss +188 -0
- package/src/sass/components/_marquee.scss +111 -0
- package/src/sass/components/_menu.scss +10 -0
- package/src/sass/components/_message-scroller.scss +66 -0
- package/src/sass/components/_message.scss +202 -0
- package/src/sass/components/_mixed-chart.scss +157 -0
- package/src/sass/components/_navigation-bar.scss +1 -0
- package/src/sass/components/_navigation-rail.scss +9 -0
- package/src/sass/components/_particles.scss +14 -0
- package/src/sass/components/_pie-chart.scss +203 -0
- package/src/sass/components/_popover.scss +112 -0
- package/src/sass/components/_questionnaire.scss +144 -0
- package/src/sass/components/_radar-chart.scss +205 -0
- package/src/sass/components/_radial-chart.scss +174 -0
- package/src/sass/components/_rich-text-editor.scss +99 -0
- package/src/sass/components/_search.scss +1 -0
- package/src/sass/components/_side-sheet.scss +1 -0
- package/src/sass/components/_skeleton.scss +81 -0
- package/src/sass/components/_snackbar.scss +1 -0
- package/src/sass/components/_stat.scss +86 -0
- package/src/sass/components/_stepper.scss +190 -0
- package/src/sass/components/_table_of_contents.scss +3 -1
- package/src/sass/components/_timeline.scss +114 -0
- package/src/sass/components/_toolbar.scss +1 -0
- package/src/sass/components/_tooltip.scss +2 -0
- package/src/sass/components/_tree.scss +139 -0
- package/src/sass/components/forms/_checkboxes.scss +7 -2
- package/src/sass/components/forms/_drop-zone.scss +128 -0
- package/src/sass/components/forms/_index.scss +2 -0
- package/src/sass/components/forms/_input-fields.scss +133 -2
- package/src/sass/components/forms/_rating.scss +165 -0
- package/src/sass/tokens/_reference.scss +3 -15
- package/src/sass/utilities/_index.scss +2 -0
- package/src/sass/utilities/_scroll-area.scss +35 -0
- package/src/sass/utilities/_shapes.scss +164 -0
- package/dist/js/chunks/chunk-3VOK6J7U.mjs.map +0 -7
- package/dist/js/chunks/chunk-CKQP6P76.mjs.map +0 -7
- package/dist/js/chunks/chunk-EC63CHXU.mjs.map +0 -7
- package/dist/js/chunks/chunk-G3I5H2U3.mjs +0 -7
- package/dist/js/chunks/chunk-G3I5H2U3.mjs.map +0 -7
- package/dist/js/chunks/chunk-GZWPXXY6.mjs.map +0 -7
- package/dist/js/chunks/chunk-H2WA4XHH.mjs.map +0 -7
- package/dist/js/chunks/chunk-HLDXZ7XB.mjs.map +0 -7
- package/dist/js/chunks/chunk-HNFSMIBY.mjs.map +0 -7
- package/dist/js/chunks/chunk-LWBQIX5J.mjs.map +0 -7
- package/dist/js/chunks/chunk-NNYP33NB.mjs.map +0 -7
- package/dist/js/chunks/chunk-PSSHQWCX.mjs +0 -100
- package/dist/js/chunks/chunk-PSSHQWCX.mjs.map +0 -7
- package/dist/js/chunks/chunk-WJXDBPXQ.mjs.map +0 -7
- package/dist/js/chunks/chunk-WWPAPHMA.mjs +0 -1
- package/dist/js/chunks/chunk-YSBCHYXP.mjs.map +0 -7
- /package/dist/js/chunks/{chunk-HB6IW7QQ.mjs.map → chunk-3GVFZ4XO.mjs.map} +0 -0
- /package/dist/js/chunks/{chunk-DLWYKFEI.mjs.map → chunk-CUQPB25U.mjs.map} +0 -0
- /package/dist/js/chunks/{chunk-6WLSDZ4O.mjs.map → chunk-DAIU5SLG.mjs.map} +0 -0
- /package/dist/js/chunks/{chunk-RFZZCYVU.mjs.map → chunk-KY6U46SM.mjs.map} +0 -0
- /package/dist/js/chunks/{chunk-UBLRRJ7N.mjs.map → chunk-PM655FXL.mjs.map} +0 -0
- /package/dist/js/chunks/{chunk-IFXVDDDH.mjs.map → chunk-QERLECVU.mjs.map} +0 -0
- /package/dist/js/chunks/{chunk-UNEMYWEW.mjs.map → chunk-S3XDFGSZ.mjs.map} +0 -0
- /package/dist/js/chunks/{chunk-UAEAQBRC.mjs.map → chunk-X7OCHZVF.mjs.map} +0 -0
- /package/dist/js/chunks/{chunk-QVVEWBOF.mjs.map → chunk-Z2NRKI7R.mjs.map} +0 -0
- /package/dist/js/chunks/{chunk-PIJ2TAR3.mjs.map → chunk-ZAS25YFD.mjs.map} +0 -0
- /package/dist/js/chunks/{chunk-WWPAPHMA.mjs.map → chunk-ZKZKNSLC.mjs.map} +0 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/ts/components/tooltip.ts"],
|
|
4
|
+
"sourcesContent": ["import { Utils } from '../core/utils';\nimport { Component, BaseOptions, InitElements, InitElement } from '../core/component';\n\nexport type TooltipPosition = 'top' | 'right' | 'bottom' | 'left';\n\nexport interface TooltipOptions extends BaseOptions {\n /**\n * Delay time before tooltip disappears.\n * @default 200\n */\n exitDelay: number;\n /**\n * Delay time before tooltip appears.\n * @default 0\n */\n enterDelay: number;\n /**\n * Element Id for the tooltip.\n * @default \"\"\n */\n tooltipId?: string;\n /**\n * Text string for the tooltip.\n * @default \"\"\n */\n text: string;\n /**\n * Set distance tooltip appears away from its activator\n * excluding transitionMovement.\n * @default 4\n */\n margin: number;\n /**\n * Enter transition duration.\n * @default 300\n */\n inDuration: number;\n /**\n * Opacity of the tooltip.\n * @default 1\n */\n opacity: number;\n /**\n * Exit transition duration.\n * @default 250\n */\n outDuration: number;\n /**\n * Set the direction of the tooltip.\n * @default 'bottom'\n */\n position: TooltipPosition;\n /**\n * Amount in px that the tooltip moves during its transition.\n * @default 10\n */\n transitionMovement: number;\n}\n\nconst _defaults: TooltipOptions = {\n exitDelay: 200,\n enterDelay: 0,\n text: '',\n margin: 4,\n inDuration: 250,\n outDuration: 200,\n position: 'bottom' as TooltipPosition,\n transitionMovement: 10,\n opacity: 1\n};\n\nexport class Tooltip extends Component<TooltipOptions> {\n /**\n * If tooltip is open.\n */\n isOpen: boolean;\n /**\n * If tooltip is hovered.\n */\n isHovered: boolean;\n /**\n * If tooltip is focused.\n */\n isFocused: boolean;\n tooltipEl: HTMLElement;\n private _exitDelayTimeout: ReturnType<typeof setTimeout>;\n private _enterDelayTimeout: ReturnType<typeof setTimeout>;\n private _animationTimeout: ReturnType<typeof setTimeout>;\n private _originalContent: {\n el: HTMLElement;\n marker: Comment;\n display: string;\n priority: string;\n hidden: string | null;\n contentClass: boolean;\n };\n xMovement: number;\n yMovement: number;\n\n constructor(el: HTMLElement, options: Partial<TooltipOptions>) {\n super(el, options, Tooltip);\n this.el['Expressive_Tooltip'] = this;\n\n this.options = {\n ...Tooltip.defaults,\n ...this._getAttributeOptions(),\n ...options\n };\n\n this.isOpen = false;\n this.isHovered = false;\n this.isFocused = false;\n this._appendTooltipEl();\n this._setupEventHandlers();\n }\n\n static get defaults(): TooltipOptions {\n return _defaults;\n }\n\n /**\n * Initializes instance of Tooltip.\n * @param el HTML element.\n * @param options Component options.\n */\n static init(el: HTMLElement, options?: Partial<TooltipOptions>): Tooltip;\n /**\n * Initializes instances of Tooltip.\n * @param els HTML elements.\n * @param options Component options.\n */\n static init(els: InitElements<InitElement>, options?: Partial<TooltipOptions>): Tooltip[];\n /**\n * Initializes instances of Tooltip.\n * @param els HTML elements.\n * @param options Component options.\n */\n static init(\n els: HTMLElement | InitElements<InitElement>,\n options: Partial<TooltipOptions> = {}\n ): Tooltip | Tooltip[] {\n return super.init(els, options, Tooltip);\n }\n\n static getInstance(el: HTMLElement): Tooltip {\n return el['Expressive_Tooltip'];\n }\n\n destroy() {\n clearTimeout(this._enterDelayTimeout);\n clearTimeout(this._exitDelayTimeout);\n clearTimeout(this._animationTimeout);\n this.isOpen = false;\n this.isHovered = false;\n this.isFocused = false;\n const descriptions = this.el.getAttribute('aria-describedby')?.split(/\\s+/);\n if (descriptions?.includes(this.tooltipEl.id)) {\n const remaining = descriptions.filter(id => id && id !== this.tooltipEl.id).join(' ');\n if (remaining) this.el.setAttribute('aria-describedby', remaining);\n else this.el.removeAttribute('aria-describedby');\n }\n if (this._originalContent) {\n const { el, marker, display, priority, hidden, contentClass } = this._originalContent;\n el.style.setProperty('display', display, priority);\n if (hidden === null) el.removeAttribute('hidden');\n else el.setAttribute('hidden', hidden);\n el.classList.toggle('tooltip-content', contentClass);\n if (!el.className) el.removeAttribute('class');\n marker.replaceWith(el);\n this._originalContent = null;\n }\n this.tooltipEl.remove();\n this._removeEventHandlers();\n this.el['Expressive_Tooltip'] = undefined;\n }\n\n _appendTooltipEl() {\n this.tooltipEl = document.createElement('div');\n this.tooltipEl.classList.add('tooltip');\n this.tooltipEl.id = `tooltip-${Utils.guid()}`;\n this.tooltipEl.setAttribute('role', 'tooltip');\n this.el.setAttribute('aria-describedby', [this.el.getAttribute('aria-describedby'), this.tooltipEl.id].filter(Boolean).join(' '));\n\n const tooltipContentEl = this.options.tooltipId\n ? Utils.getElementById(this.el, this.options.tooltipId)\n : document.createElement('div');\n if (this.options.tooltipId) {\n this.tooltipEl.classList.add('rich');\n const marker = document.createComment('');\n tooltipContentEl.before(marker);\n this._originalContent = {\n el: tooltipContentEl, marker,\n display: tooltipContentEl.style.display,\n priority: tooltipContentEl.style.getPropertyPriority('display'),\n hidden: tooltipContentEl.getAttribute('hidden'),\n contentClass: tooltipContentEl.classList.contains('tooltip-content')\n };\n tooltipContentEl.removeAttribute('hidden');\n }\n tooltipContentEl.style.display = '';\n tooltipContentEl.classList.add('tooltip-content');\n this._setTooltipContent(tooltipContentEl);\n this.tooltipEl.appendChild(tooltipContentEl);\n Utils.portalRoot(this.el).appendChild(this.tooltipEl);\n }\n\n _setTooltipContent(tooltipContentEl: HTMLElement) {\n if (this.options.tooltipId) return;\n tooltipContentEl.innerText = this.options.text;\n }\n\n _updateTooltipContent() {\n this._setTooltipContent(this.tooltipEl.querySelector('.tooltip-content'));\n }\n\n _setupEventHandlers() {\n for (const el of [this.el, this.tooltipEl]) {\n el.addEventListener('mouseenter', this._handleMouseEnter);\n el.addEventListener('mouseleave', this._handleMouseLeave);\n el.addEventListener('focus', this._handleFocus, true);\n el.addEventListener('blur', this._handleBlur, true);\n }\n }\n\n _removeEventHandlers() {\n for (const el of [this.el, this.tooltipEl]) {\n el.removeEventListener('mouseenter', this._handleMouseEnter);\n el.removeEventListener('mouseleave', this._handleMouseLeave);\n el.removeEventListener('focus', this._handleFocus, true);\n el.removeEventListener('blur', this._handleBlur, true);\n }\n // Escape is only listened for while open; see open() and close().\n this.el.ownerDocument.removeEventListener('keydown', this._handleKeydown, true);\n }\n\n /**\n * Show tooltip.\n */\n open = (isManual: boolean = true) => {\n if (this.isOpen) return;\n clearTimeout(this._exitDelayTimeout);\n clearTimeout(this._animationTimeout);\n this.isOpen = true;\n this.el.ownerDocument.addEventListener('keydown', this._handleKeydown, true);\n // Update tooltip content with HTML attribute options\n this.options = { ...this.options, ...this._getAttributeOptions() };\n this._updateTooltipContent();\n this._setEnterDelayTimeout(isManual);\n };\n\n /**\n * Hide tooltip.\n */\n close = () => {\n if (!this.isOpen) return;\n this.isHovered = false;\n this.isFocused = false;\n this.isOpen = false;\n this.el.ownerDocument.removeEventListener('keydown', this._handleKeydown, true);\n clearTimeout(this._enterDelayTimeout);\n this._setExitDelayTimeout();\n };\n\n _setExitDelayTimeout() {\n clearTimeout(this._exitDelayTimeout);\n this._exitDelayTimeout = setTimeout(() => {\n if (this.isHovered || this.isFocused) return;\n this._animateOut();\n }, this.options.exitDelay);\n }\n\n _setEnterDelayTimeout(isManual) {\n clearTimeout(this._enterDelayTimeout);\n this._enterDelayTimeout = setTimeout(() => {\n if (!this.isHovered && !this.isFocused && !isManual) return;\n this._animateIn();\n }, this.options.enterDelay);\n }\n\n _positionTooltip() {\n const tooltip: HTMLElement = this.tooltipEl;\n const origin = this.el as HTMLElement,\n originHeight = origin.offsetHeight,\n originWidth = origin.offsetWidth,\n tooltipHeight = tooltip.offsetHeight,\n tooltipWidth = tooltip.offsetWidth,\n margin = this.options.margin;\n\n this.xMovement = 0;\n this.yMovement = 0;\n\n const originRect = origin.getBoundingClientRect();\n let targetTop = originRect.top + Utils.getDocumentScrollTop();\n let targetLeft = originRect.left + Utils.getDocumentScrollLeft();\n if (this.options.position === 'top') {\n targetTop += -tooltipHeight - margin;\n targetLeft += originWidth / 2 - tooltipWidth / 2;\n this.yMovement = -this.options.transitionMovement;\n } else if (this.options.position === 'right') {\n targetTop += originHeight / 2 - tooltipHeight / 2;\n targetLeft += originWidth + margin;\n this.xMovement = this.options.transitionMovement;\n } else if (this.options.position === 'left') {\n targetTop += originHeight / 2 - tooltipHeight / 2;\n targetLeft += -tooltipWidth - margin;\n this.xMovement = -this.options.transitionMovement;\n } else {\n targetTop += originHeight + margin;\n targetLeft += originWidth / 2 - tooltipWidth / 2;\n this.yMovement = this.options.transitionMovement;\n }\n\n const newCoordinates = this._repositionWithinScreen(\n targetLeft,\n targetTop,\n tooltipWidth,\n tooltipHeight\n );\n\n tooltip.style.setProperty('--md-comp-tooltip-anchor-gap', `${Math.max(0, margin) + Math.abs(this.options.transitionMovement)}px`);\n tooltip.style.top = newCoordinates.y + 'px';\n tooltip.style.left = newCoordinates.x + 'px';\n }\n\n _repositionWithinScreen(x: number, y: number, width: number, height: number) {\n return Utils._repositionWithinScreen(\n x, y, width, height, this.options.margin, this.options.transitionMovement, 'center'\n );\n }\n\n _animateIn() { this._animate(true); }\n\n _animateOut() { this._animate(false); }\n\n _animate(show: boolean) {\n const style = this.tooltipEl.style;\n const reduce = this.el.ownerDocument.defaultView.matchMedia('(prefers-reduced-motion: reduce)').matches;\n const duration = reduce ? 0 : show ? this.options.inDuration : this.options.outDuration;\n if (show) {\n this._positionTooltip();\n style.visibility = 'visible';\n style.overflow = 'visible';\n }\n style.transition = `transform ${duration}ms ease-out, opacity ${duration}ms ease-out`;\n clearTimeout(this._animationTimeout);\n this._animationTimeout = setTimeout(() => {\n style.transform = show && !reduce ? `translateX(${this.xMovement}px) translateY(${this.yMovement}px)` : 'none';\n style.opacity = show ? (this.options.opacity || 1).toString() : '0';\n if (!show) this._animationTimeout = setTimeout(() => {\n style.visibility = 'hidden';\n style.overflow = '';\n }, duration);\n }, 1);\n }\n\n _handleMouseEnter = () => {\n this.isHovered = true;\n this.open(false);\n };\n\n _handleMouseLeave = (event: MouseEvent) => {\n const next = event.relatedTarget as Node | null;\n if (next && (this.el.contains(next) || this.tooltipEl.contains(next))) return;\n this.isHovered = false;\n if (!this.isFocused) this.close();\n };\n\n _handleKeydown = (event: KeyboardEvent) => {\n if (event.key !== 'Escape' || !this.isOpen) return;\n event.preventDefault();\n event.stopPropagation();\n this.close();\n clearTimeout(this._exitDelayTimeout);\n this._animateOut();\n };\n\n // Focus opens the tooltip only when it arrived by keyboard: a pointer press\n // on the trigger also focuses it, and _handleMouseEnter already covers that.\n _handleFocus = (event: FocusEvent) => {\n if ((event.target as HTMLElement).matches(':focus-visible')) {\n this.isFocused = true;\n this.open(false);\n }\n };\n\n _handleBlur = (event: FocusEvent) => {\n const next = event.relatedTarget as Node | null;\n if (next && (this.el.contains(next) || this.tooltipEl.contains(next))) return;\n this.isFocused = false;\n if (!this.isHovered) this.close();\n };\n\n _getAttributeOptions(): Partial<TooltipOptions> {\n const attributeOptions: Partial<TooltipOptions> = {};\n const tooltipTextOption = this.el.getAttribute('data-tooltip');\n const tooltipId = this.el.getAttribute('data-tooltip-id');\n const positionOption = this.el.getAttribute('data-position');\n if (tooltipTextOption) {\n attributeOptions.text = tooltipTextOption;\n }\n if (positionOption) {\n attributeOptions.position = positionOption as TooltipPosition;\n }\n if (tooltipId) {\n attributeOptions.tooltipId = tooltipId;\n }\n\n return attributeOptions;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -176,9 +176,9 @@ var _Utils = class _Utils {
|
|
|
176
176
|
static throttle(func, wait) {
|
|
177
177
|
let timeout = null;
|
|
178
178
|
let lastArgs = null;
|
|
179
|
-
let previous =
|
|
179
|
+
let previous = -Infinity;
|
|
180
180
|
return (...args) => {
|
|
181
|
-
const now =
|
|
181
|
+
const now = performance.now();
|
|
182
182
|
const remaining = wait - (now - previous);
|
|
183
183
|
if (remaining <= 0) {
|
|
184
184
|
clearTimeout(timeout);
|
|
@@ -189,7 +189,7 @@ var _Utils = class _Utils {
|
|
|
189
189
|
} else if (!timeout) {
|
|
190
190
|
lastArgs = args;
|
|
191
191
|
timeout = setTimeout(() => {
|
|
192
|
-
previous =
|
|
192
|
+
previous = performance.now();
|
|
193
193
|
timeout = null;
|
|
194
194
|
func(...lastArgs ?? []);
|
|
195
195
|
lastArgs = null;
|
|
@@ -325,4 +325,4 @@ var Utils = _Utils;
|
|
|
325
325
|
export {
|
|
326
326
|
Utils
|
|
327
327
|
};
|
|
328
|
-
//# sourceMappingURL=chunk-
|
|
328
|
+
//# sourceMappingURL=chunk-MNPROCZF.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/ts/core/utils.ts"],
|
|
4
|
-
"sourcesContent": ["/** The part of a `DOMRect` the positioning helpers below actually read. */\ntype Bounding = Pick<DOMRect, 'left' | 'top' | 'width' | 'height'>;\n\n/** Which edges of a container a box has crossed. */\ntype Edges = { top: boolean; right: boolean; bottom: boolean; left: boolean };\n\n/**\n * Class with utilitary functions for global usage.\n */\nexport class Utils {\n /**\n * Key maps. One `KeyboardEvent.key` value each: the second entries these used\n * to carry (`Esc`, `Up`, `Del`, ...) were IE and Edge Legacy spellings, and\n * the support baseline excludes both.\n */\n static keys = {\n TAB: 'Tab',\n ENTER: 'Enter',\n ESC: 'Escape',\n BACKSPACE: 'Backspace',\n ARROW_UP: 'ArrowUp',\n ARROW_DOWN: 'ArrowDown',\n ARROW_LEFT: 'ArrowLeft',\n ARROW_RIGHT: 'ArrowRight',\n DELETE: 'Delete'\n };\n\n /**\n * Run `callback` once the document is parsed.\n *\n * The document-level behaviors all used a bare DOMContentLoaded listener,\n * which never fires if the bundle is loaded after that event - an async or\n * deferred script tag, or a dynamic import. Checking readyState first covers\n * both orders.\n */\n static onDocumentReady(callback: () => void): void {\n if (typeof document === 'undefined') return;\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', callback, { once: true });\n } else {\n callback();\n }\n }\n\n /**\n * Generates a unique string identifier. Every caller prefixes the result and\n * uses it as an element id, and a UUID is valid in that position.\n *\n * `randomUUID` is exposed only in secure contexts, and a dev server on a\n * plain-HTTP LAN address - the usual way a page is opened on a phone - is\n * not one. Without the fallback every component that stamps an id would\n * throw there. The ids only have to be unique within one document, so the\n * fallback does not need to be cryptographic.\n */\n static guid(): string {\n return (\n globalThis.crypto?.randomUUID?.() ??\n `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`\n );\n }\n\n /**\n * Where an element created at runtime has to be appended to stay in the same\n * tree as `el`. A stylesheet adopted into a shadow root cannot match a node\n * in the document, so a portal that lands on `document.body` is unstyled\n * whenever its origin lives in a shadow root (adr/0002).\n *\n * Light-DOM elements still get `document.body`, which is what escapes an\n * ancestor's `overflow: hidden` and stacking context. Inside a shadow root\n * both hazards come back: the host's own ancestors still form the containing\n * block, so a transformed ancestor re-anchors `position: fixed` and a\n * positioned one re-anchors `position: absolute`.\n */\n static portalRoot(el: Node): HTMLElement | ShadowRoot {\n const root = el.getRootNode() as ShadowRoot;\n // Only a ShadowRoot has a host; a Document or a detached subtree does not.\n return root.host ? root : document.body;\n }\n\n /**\n * `getElementById` against the tree `el` lives in. The mirror of\n * `portalRoot`: an id in a shadow root is invisible to\n * `document.getElementById`, so a trigger and its target inside the same\n * root could not find each other at all.\n *\n * Falls back to the document for a detached `el`, whose root is a plain\n * element with no lookup of its own.\n */\n static getElementById(el: Node, id: string): HTMLElement {\n const root = el.getRootNode() as Document | ShadowRoot;\n return (root.getElementById ? root.getElementById(id) : document.getElementById(id)) as HTMLElement;\n }\n\n /**\n * Checks for exceeded edges\n * @param container Container element.\n * @param bounding Bounding rect.\n * @param offset Element offset.\n */\n static checkWithinContainer(container: HTMLElement, bounding: Bounding, offset: number): Edges {\n const edges = {\n top: false,\n right: false,\n bottom: false,\n left: false\n };\n\n const containerRect = container.getBoundingClientRect();\n // If body element is smaller than viewport, use viewport height instead.\n const containerBottom =\n container === document.body\n ? Math.max(containerRect.bottom, window.innerHeight)\n : containerRect.bottom;\n\n const scrollLeft = container.scrollLeft;\n const scrollTop = container.scrollTop;\n\n const scrolledX = bounding.left - scrollLeft;\n const scrolledY = bounding.top - scrollTop;\n\n // Check for container and viewport for each edge\n if (scrolledX < containerRect.left + offset || scrolledX < offset) {\n edges.left = true;\n }\n\n if (\n scrolledX + bounding.width > containerRect.right - offset ||\n scrolledX + bounding.width > window.innerWidth - offset\n ) {\n edges.right = true;\n }\n\n if (scrolledY < containerRect.top + offset || scrolledY < offset) {\n edges.top = true;\n }\n\n if (\n scrolledY + bounding.height > containerBottom - offset ||\n scrolledY + bounding.height > window.innerHeight - offset\n ) {\n edges.bottom = true;\n }\n\n return edges;\n }\n\n /**\n * Checks if element can be aligned in multiple directions.\n * @param el Element to be inspected.\n * @param container Container element.\n * @param bounding Bounding rect.\n * @param offset Element offset.\n */\n static checkPossibleAlignments(\n el: HTMLElement,\n container: HTMLElement,\n bounding: Bounding,\n offset: number\n ) {\n const canAlign: {\n top: boolean;\n right: boolean;\n bottom: boolean;\n left: boolean;\n spaceOnTop: number;\n spaceOnRight: number;\n spaceOnBottom: number;\n spaceOnLeft: number;\n } = {\n top: true,\n right: true,\n bottom: true,\n left: true,\n spaceOnTop: null,\n spaceOnRight: null,\n spaceOnBottom: null,\n spaceOnLeft: null\n };\n\n const containerAllowsOverflow = getComputedStyle(container).overflow === 'visible';\n const containerRect = container.getBoundingClientRect();\n const containerHeight = Math.min(containerRect.height, window.innerHeight);\n const containerWidth = Math.min(containerRect.width, window.innerWidth);\n const elOffsetRect = el.getBoundingClientRect();\n\n const scrollLeft = container.scrollLeft;\n const scrollTop = container.scrollTop;\n\n const scrolledX = bounding.left - scrollLeft;\n const scrolledYTopEdge = bounding.top - scrollTop;\n const scrolledYBottomEdge = bounding.top + elOffsetRect.height - scrollTop;\n\n // Check for container and viewport for left\n canAlign.spaceOnRight = !containerAllowsOverflow\n ? containerWidth - (scrolledX + bounding.width)\n : window.innerWidth - (elOffsetRect.left + bounding.width);\n if (canAlign.spaceOnRight < 0) {\n canAlign.left = false;\n }\n\n // Check for container and viewport for Right\n canAlign.spaceOnLeft = !containerAllowsOverflow\n ? scrolledX - bounding.width + elOffsetRect.width\n : elOffsetRect.right - bounding.width;\n if (canAlign.spaceOnLeft < 0) {\n canAlign.right = false;\n }\n\n // Check for container and viewport for Top\n canAlign.spaceOnBottom = !containerAllowsOverflow\n ? containerHeight - (scrolledYTopEdge + bounding.height + offset)\n : window.innerHeight - (elOffsetRect.top + bounding.height + offset);\n if (canAlign.spaceOnBottom < 0) {\n canAlign.top = false;\n }\n\n // Check for container and viewport for Bottom\n canAlign.spaceOnTop = !containerAllowsOverflow\n ? scrolledYBottomEdge - (bounding.height - offset)\n : elOffsetRect.bottom - (bounding.height + offset);\n if (canAlign.spaceOnTop < 0) {\n canAlign.bottom = false;\n }\n\n return canAlign;\n }\n\n /**\n * Retrieves target element id from trigger.\n * @param trigger Trigger element.\n */\n static getIdFromTrigger(trigger: HTMLElement): string {\n let id = trigger.dataset.target;\n if (!id) {\n id = trigger.getAttribute('href');\n return id ? id.slice(1) : '';\n }\n return id;\n }\n\n /**\n * Retrieves document scroll postion from top.\n */\n static getDocumentScrollTop(): number {\n return window.scrollY;\n }\n\n /**\n * Retrieves document scroll postion from left.\n */\n static getDocumentScrollLeft(): number {\n return window.scrollX;\n }\n\n /**\n * Trailing-edge throttle: run `func` now, then at most once per `wait` ms\n * with the most recent arguments.\n *\n * This used to carry its underscore ancestor's whole configuration - leading\n * and trailing switches, a result passthrough - and both call sites are\n * resize handlers passing nothing but a function and a delay.\n *\n * Assign the return value once. `x = Utils.throttle(fn, 200)` is right;\n * wrapping it in an arrow builds a fresh closure per event and never calls\n * it, which is how resize handling was dead in three components.\n */\n static throttle(func: (...args: unknown[]) => void, wait: number) {\n let timeout: ReturnType<typeof setTimeout> = null;\n let lastArgs: unknown[] = null;\n let previous = 0;\n\n return (...args: unknown[]) => {\n const now = Date.now();\n const remaining = wait - (now - previous);\n if (remaining <= 0) {\n clearTimeout(timeout);\n timeout = null;\n previous = now;\n lastArgs = null;\n func(...args);\n } else if (!timeout) {\n // The trailing call replays the most recent arguments; without this it\n // used to fire with none at all.\n lastArgs = args;\n timeout = setTimeout(() => {\n previous = Date.now();\n timeout = null;\n func(...(lastArgs ?? []));\n lastArgs = null;\n }, remaining);\n } else {\n lastArgs = args;\n }\n };\n }\n\n /**\n * Renders confirm/close buttons with callback function\n */\n static createConfirmationContainer(\n container: HTMLElement,\n confirmText: string,\n cancelText: string,\n onConfirm: (Function: object) => void,\n onCancel: (Function: object) => void\n ): void {\n const confirmationButtonsContainer = document.createElement('div');\n confirmationButtonsContainer.classList.add('confirmation-btns');\n container.append(confirmationButtonsContainer);\n\n this.createButton(confirmationButtonsContainer, cancelText, ['btn-cancel'], true, onCancel);\n this.createButton(confirmationButtonsContainer, confirmText, ['btn-confirm'], true, onConfirm);\n }\n\n /**\n * Renders a button with optional callback function\n */\n static createButton(\n container: HTMLElement,\n text: string,\n className: string[] = [],\n visibility: boolean = true,\n callback: (Function: object) => void = null\n ): void {\n className = className.concat(['text']);\n const button = document.createElement('button');\n button.className = className.join(' ');\n button.style.visibility = visibility ? 'visible' : 'hidden';\n button.type = 'button';\n button.tabIndex = !!visibility ? 0 : -1;\n button.innerText = text;\n if (typeof callback === 'function') button.addEventListener('click', callback);\n container.append(button);\n }\n\n static _setAbsolutePosition(\n origin: HTMLElement,\n container: HTMLElement,\n position: string,\n margin: number,\n transitionMovement: number,\n align: string = 'center'\n ) {\n const originHeight = origin.offsetHeight,\n originWidth = origin.offsetWidth,\n containerHeight = container.offsetHeight,\n containerWidth = container.offsetWidth,\n originRect = origin.getBoundingClientRect(); // one rect, read twice below\n let xMovement = 0,\n yMovement = 0,\n targetTop = originRect.top + Utils.getDocumentScrollTop(),\n targetLeft = originRect.left + Utils.getDocumentScrollLeft();\n\n if (position === 'top') {\n targetTop += -containerHeight - margin;\n if (align === 'center') {\n targetLeft += originWidth / 2 - containerWidth / 2; // This is center align\n }\n yMovement = -transitionMovement;\n } else if (position === 'right') {\n targetTop += originHeight / 2 - containerHeight / 2;\n targetLeft = originWidth + margin;\n xMovement = transitionMovement;\n } else if (position === 'left') {\n targetTop += originHeight / 2 - containerHeight / 2;\n targetLeft = -containerWidth - margin;\n xMovement = -transitionMovement;\n } else {\n targetTop += originHeight + margin;\n if (align === 'center') {\n targetLeft += originWidth / 2 - containerWidth / 2; // This is center align\n }\n yMovement = transitionMovement;\n }\n if (align === 'right') {\n targetLeft += originWidth - containerWidth - margin;\n }\n\n const newCoordinates = Utils._repositionWithinScreen(\n targetLeft,\n targetTop,\n containerWidth,\n containerHeight,\n margin,\n transitionMovement,\n align\n );\n\n const parent = container.offsetParent as HTMLElement;\n // A static body uses document coordinates; positioned parents use their\n // padding edge, including borders and local scrolling.\n if (parent && (parent !== document.body || getComputedStyle(parent).position !== 'static')) {\n const rect = parent.getBoundingClientRect();\n const root = parent === document.scrollingElement;\n newCoordinates.x -= rect.left + Utils.getDocumentScrollLeft() + parent.clientLeft - (root ? 0 : parent.scrollLeft);\n newCoordinates.y -= rect.top + Utils.getDocumentScrollTop() + parent.clientTop - (root ? 0 : parent.scrollTop);\n }\n\n container.style.top = newCoordinates.y + 'px';\n container.style.left = newCoordinates.x + 'px';\n\n return {x: xMovement, y: yMovement};\n }\n\n static _repositionWithinScreen(\n x: number,\n y: number,\n width: number,\n height: number,\n margin: number,\n transitionMovement: number,\n align: string\n ) {\n const scrollLeft = Utils.getDocumentScrollLeft();\n const scrollTop = Utils.getDocumentScrollTop();\n let newX = x - scrollLeft;\n let newY = y - scrollTop;\n\n const bounding: Bounding = {\n left: newX,\n top: newY,\n width: width,\n height: height\n };\n let offset: number;\n if (align === 'left' || align == 'center') {\n offset = margin + transitionMovement;\n } else if (align === 'right') {\n offset = margin - transitionMovement;\n }\n const edges = Utils.checkWithinContainer(document.body, bounding, offset);\n\n if (edges.left) {\n newX = offset;\n } else if (edges.right) {\n newX -= newX + width - window.innerWidth;\n }\n if (edges.top) {\n newY = offset;\n } else if (edges.bottom) {\n newY -= newY + height - window.innerHeight;\n }\n return {\n x: newX + scrollLeft,\n y: newY + scrollTop\n };\n }\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["/** The part of a `DOMRect` the positioning helpers below actually read. */\ntype Bounding = Pick<DOMRect, 'left' | 'top' | 'width' | 'height'>;\n\n/** Which edges of a container a box has crossed. */\ntype Edges = { top: boolean; right: boolean; bottom: boolean; left: boolean };\n\n/**\n * Class with utilitary functions for global usage.\n */\nexport class Utils {\n /**\n * Key maps. One `KeyboardEvent.key` value each: the second entries these used\n * to carry (`Esc`, `Up`, `Del`, ...) were IE and Edge Legacy spellings, and\n * the support baseline excludes both.\n */\n static keys = {\n TAB: 'Tab',\n ENTER: 'Enter',\n ESC: 'Escape',\n BACKSPACE: 'Backspace',\n ARROW_UP: 'ArrowUp',\n ARROW_DOWN: 'ArrowDown',\n ARROW_LEFT: 'ArrowLeft',\n ARROW_RIGHT: 'ArrowRight',\n DELETE: 'Delete'\n };\n\n /**\n * Run `callback` once the document is parsed.\n *\n * The document-level behaviors all used a bare DOMContentLoaded listener,\n * which never fires if the bundle is loaded after that event - an async or\n * deferred script tag, or a dynamic import. Checking readyState first covers\n * both orders.\n */\n static onDocumentReady(callback: () => void): void {\n if (typeof document === 'undefined') return;\n if (document.readyState === 'loading') {\n document.addEventListener('DOMContentLoaded', callback, { once: true });\n } else {\n callback();\n }\n }\n\n /**\n * Generates a unique string identifier. Every caller prefixes the result and\n * uses it as an element id, and a UUID is valid in that position.\n *\n * `randomUUID` is exposed only in secure contexts, and a dev server on a\n * plain-HTTP LAN address - the usual way a page is opened on a phone - is\n * not one. Without the fallback every component that stamps an id would\n * throw there. The ids only have to be unique within one document, so the\n * fallback does not need to be cryptographic.\n */\n static guid(): string {\n return (\n globalThis.crypto?.randomUUID?.() ??\n `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`\n );\n }\n\n /**\n * Where an element created at runtime has to be appended to stay in the same\n * tree as `el`. A stylesheet adopted into a shadow root cannot match a node\n * in the document, so a portal that lands on `document.body` is unstyled\n * whenever its origin lives in a shadow root (adr/0002).\n *\n * Light-DOM elements still get `document.body`, which is what escapes an\n * ancestor's `overflow: hidden` and stacking context. Inside a shadow root\n * both hazards come back: the host's own ancestors still form the containing\n * block, so a transformed ancestor re-anchors `position: fixed` and a\n * positioned one re-anchors `position: absolute`.\n */\n static portalRoot(el: Node): HTMLElement | ShadowRoot {\n const root = el.getRootNode() as ShadowRoot;\n // Only a ShadowRoot has a host; a Document or a detached subtree does not.\n return root.host ? root : document.body;\n }\n\n /**\n * `getElementById` against the tree `el` lives in. The mirror of\n * `portalRoot`: an id in a shadow root is invisible to\n * `document.getElementById`, so a trigger and its target inside the same\n * root could not find each other at all.\n *\n * Falls back to the document for a detached `el`, whose root is a plain\n * element with no lookup of its own.\n */\n static getElementById(el: Node, id: string): HTMLElement {\n const root = el.getRootNode() as Document | ShadowRoot;\n return (root.getElementById ? root.getElementById(id) : document.getElementById(id)) as HTMLElement;\n }\n\n /**\n * Checks for exceeded edges\n * @param container Container element.\n * @param bounding Bounding rect.\n * @param offset Element offset.\n */\n static checkWithinContainer(container: HTMLElement, bounding: Bounding, offset: number): Edges {\n const edges = {\n top: false,\n right: false,\n bottom: false,\n left: false\n };\n\n const containerRect = container.getBoundingClientRect();\n // If body element is smaller than viewport, use viewport height instead.\n const containerBottom =\n container === document.body\n ? Math.max(containerRect.bottom, window.innerHeight)\n : containerRect.bottom;\n\n const scrollLeft = container.scrollLeft;\n const scrollTop = container.scrollTop;\n\n const scrolledX = bounding.left - scrollLeft;\n const scrolledY = bounding.top - scrollTop;\n\n // Check for container and viewport for each edge\n if (scrolledX < containerRect.left + offset || scrolledX < offset) {\n edges.left = true;\n }\n\n if (\n scrolledX + bounding.width > containerRect.right - offset ||\n scrolledX + bounding.width > window.innerWidth - offset\n ) {\n edges.right = true;\n }\n\n if (scrolledY < containerRect.top + offset || scrolledY < offset) {\n edges.top = true;\n }\n\n if (\n scrolledY + bounding.height > containerBottom - offset ||\n scrolledY + bounding.height > window.innerHeight - offset\n ) {\n edges.bottom = true;\n }\n\n return edges;\n }\n\n /**\n * Checks if element can be aligned in multiple directions.\n * @param el Element to be inspected.\n * @param container Container element.\n * @param bounding Bounding rect.\n * @param offset Element offset.\n */\n static checkPossibleAlignments(\n el: HTMLElement,\n container: HTMLElement,\n bounding: Bounding,\n offset: number\n ) {\n const canAlign: {\n top: boolean;\n right: boolean;\n bottom: boolean;\n left: boolean;\n spaceOnTop: number;\n spaceOnRight: number;\n spaceOnBottom: number;\n spaceOnLeft: number;\n } = {\n top: true,\n right: true,\n bottom: true,\n left: true,\n spaceOnTop: null,\n spaceOnRight: null,\n spaceOnBottom: null,\n spaceOnLeft: null\n };\n\n const containerAllowsOverflow = getComputedStyle(container).overflow === 'visible';\n const containerRect = container.getBoundingClientRect();\n const containerHeight = Math.min(containerRect.height, window.innerHeight);\n const containerWidth = Math.min(containerRect.width, window.innerWidth);\n const elOffsetRect = el.getBoundingClientRect();\n\n const scrollLeft = container.scrollLeft;\n const scrollTop = container.scrollTop;\n\n const scrolledX = bounding.left - scrollLeft;\n const scrolledYTopEdge = bounding.top - scrollTop;\n const scrolledYBottomEdge = bounding.top + elOffsetRect.height - scrollTop;\n\n // Check for container and viewport for left\n canAlign.spaceOnRight = !containerAllowsOverflow\n ? containerWidth - (scrolledX + bounding.width)\n : window.innerWidth - (elOffsetRect.left + bounding.width);\n if (canAlign.spaceOnRight < 0) {\n canAlign.left = false;\n }\n\n // Check for container and viewport for Right\n canAlign.spaceOnLeft = !containerAllowsOverflow\n ? scrolledX - bounding.width + elOffsetRect.width\n : elOffsetRect.right - bounding.width;\n if (canAlign.spaceOnLeft < 0) {\n canAlign.right = false;\n }\n\n // Check for container and viewport for Top\n canAlign.spaceOnBottom = !containerAllowsOverflow\n ? containerHeight - (scrolledYTopEdge + bounding.height + offset)\n : window.innerHeight - (elOffsetRect.top + bounding.height + offset);\n if (canAlign.spaceOnBottom < 0) {\n canAlign.top = false;\n }\n\n // Check for container and viewport for Bottom\n canAlign.spaceOnTop = !containerAllowsOverflow\n ? scrolledYBottomEdge - (bounding.height - offset)\n : elOffsetRect.bottom - (bounding.height + offset);\n if (canAlign.spaceOnTop < 0) {\n canAlign.bottom = false;\n }\n\n return canAlign;\n }\n\n /**\n * Retrieves target element id from trigger.\n * @param trigger Trigger element.\n */\n static getIdFromTrigger(trigger: HTMLElement): string {\n let id = trigger.dataset.target;\n if (!id) {\n id = trigger.getAttribute('href');\n return id ? id.slice(1) : '';\n }\n return id;\n }\n\n /**\n * Retrieves document scroll postion from top.\n */\n static getDocumentScrollTop(): number {\n return window.scrollY;\n }\n\n /**\n * Retrieves document scroll postion from left.\n */\n static getDocumentScrollLeft(): number {\n return window.scrollX;\n }\n\n /**\n * Trailing-edge throttle: run `func` now, then at most once per `wait` ms\n * with the most recent arguments.\n *\n * This used to carry its underscore ancestor's whole configuration - leading\n * and trailing switches, a result passthrough - and both call sites are\n * resize handlers passing nothing but a function and a delay.\n *\n * Assign the return value once. `x = Utils.throttle(fn, 200)` is right;\n * wrapping it in an arrow builds a fresh closure per event and never calls\n * it, which is how resize handling was dead in three components.\n */\n static throttle(func: (...args: unknown[]) => void, wait: number) {\n let timeout: ReturnType<typeof setTimeout> = null;\n let lastArgs: unknown[] = null;\n let previous = -Infinity;\n\n return (...args: unknown[]) => {\n // performance.now() is monotonic; Date.now() follows the system clock\n // and a backward jump would hold the trailing call for as long as it jumped.\n const now = performance.now();\n const remaining = wait - (now - previous);\n if (remaining <= 0) {\n clearTimeout(timeout);\n timeout = null;\n previous = now;\n lastArgs = null;\n func(...args);\n } else if (!timeout) {\n // The trailing call replays the most recent arguments; without this it\n // used to fire with none at all.\n lastArgs = args;\n timeout = setTimeout(() => {\n previous = performance.now();\n timeout = null;\n func(...(lastArgs ?? []));\n lastArgs = null;\n }, remaining);\n } else {\n lastArgs = args;\n }\n };\n }\n\n /**\n * Renders confirm/close buttons with callback function\n */\n static createConfirmationContainer(\n container: HTMLElement,\n confirmText: string,\n cancelText: string,\n onConfirm: (Function: object) => void,\n onCancel: (Function: object) => void\n ): void {\n const confirmationButtonsContainer = document.createElement('div');\n confirmationButtonsContainer.classList.add('confirmation-btns');\n container.append(confirmationButtonsContainer);\n\n this.createButton(confirmationButtonsContainer, cancelText, ['btn-cancel'], true, onCancel);\n this.createButton(confirmationButtonsContainer, confirmText, ['btn-confirm'], true, onConfirm);\n }\n\n /**\n * Renders a button with optional callback function\n */\n static createButton(\n container: HTMLElement,\n text: string,\n className: string[] = [],\n visibility: boolean = true,\n callback: (Function: object) => void = null\n ): void {\n className = className.concat(['text']);\n const button = document.createElement('button');\n button.className = className.join(' ');\n button.style.visibility = visibility ? 'visible' : 'hidden';\n button.type = 'button';\n button.tabIndex = !!visibility ? 0 : -1;\n button.innerText = text;\n if (typeof callback === 'function') button.addEventListener('click', callback);\n container.append(button);\n }\n\n static _setAbsolutePosition(\n origin: HTMLElement,\n container: HTMLElement,\n position: string,\n margin: number,\n transitionMovement: number,\n align: string = 'center'\n ) {\n const originHeight = origin.offsetHeight,\n originWidth = origin.offsetWidth,\n containerHeight = container.offsetHeight,\n containerWidth = container.offsetWidth,\n originRect = origin.getBoundingClientRect(); // one rect, read twice below\n let xMovement = 0,\n yMovement = 0,\n targetTop = originRect.top + Utils.getDocumentScrollTop(),\n targetLeft = originRect.left + Utils.getDocumentScrollLeft();\n\n if (position === 'top') {\n targetTop += -containerHeight - margin;\n if (align === 'center') {\n targetLeft += originWidth / 2 - containerWidth / 2; // This is center align\n }\n yMovement = -transitionMovement;\n } else if (position === 'right') {\n targetTop += originHeight / 2 - containerHeight / 2;\n targetLeft = originWidth + margin;\n xMovement = transitionMovement;\n } else if (position === 'left') {\n targetTop += originHeight / 2 - containerHeight / 2;\n targetLeft = -containerWidth - margin;\n xMovement = -transitionMovement;\n } else {\n targetTop += originHeight + margin;\n if (align === 'center') {\n targetLeft += originWidth / 2 - containerWidth / 2; // This is center align\n }\n yMovement = transitionMovement;\n }\n if (align === 'right') {\n targetLeft += originWidth - containerWidth - margin;\n }\n\n const newCoordinates = Utils._repositionWithinScreen(\n targetLeft,\n targetTop,\n containerWidth,\n containerHeight,\n margin,\n transitionMovement,\n align\n );\n\n const parent = container.offsetParent as HTMLElement;\n // A static body uses document coordinates; positioned parents use their\n // padding edge, including borders and local scrolling.\n if (parent && (parent !== document.body || getComputedStyle(parent).position !== 'static')) {\n const rect = parent.getBoundingClientRect();\n const root = parent === document.scrollingElement;\n newCoordinates.x -= rect.left + Utils.getDocumentScrollLeft() + parent.clientLeft - (root ? 0 : parent.scrollLeft);\n newCoordinates.y -= rect.top + Utils.getDocumentScrollTop() + parent.clientTop - (root ? 0 : parent.scrollTop);\n }\n\n container.style.top = newCoordinates.y + 'px';\n container.style.left = newCoordinates.x + 'px';\n\n return {x: xMovement, y: yMovement};\n }\n\n static _repositionWithinScreen(\n x: number,\n y: number,\n width: number,\n height: number,\n margin: number,\n transitionMovement: number,\n align: string\n ) {\n const scrollLeft = Utils.getDocumentScrollLeft();\n const scrollTop = Utils.getDocumentScrollTop();\n let newX = x - scrollLeft;\n let newY = y - scrollTop;\n\n const bounding: Bounding = {\n left: newX,\n top: newY,\n width: width,\n height: height\n };\n let offset: number;\n if (align === 'left' || align == 'center') {\n offset = margin + transitionMovement;\n } else if (align === 'right') {\n offset = margin - transitionMovement;\n }\n const edges = Utils.checkWithinContainer(document.body, bounding, offset);\n\n if (edges.left) {\n newX = offset;\n } else if (edges.right) {\n newX -= newX + width - window.innerWidth;\n }\n if (edges.top) {\n newY = offset;\n } else if (edges.bottom) {\n newY -= newY + height - window.innerHeight;\n }\n return {\n x: newX + scrollLeft,\n y: newY + scrollTop\n };\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -74,8 +74,9 @@ var _ScrollSpy = class _ScrollSpy extends Component {
|
|
|
74
74
|
const retained = elements.filter((spy) => spy.options.keepTopElementActive);
|
|
75
75
|
best = _ScrollSpy._nearestAbove(retained) ?? retained[0];
|
|
76
76
|
}
|
|
77
|
-
const next = best ? _ScrollSpy._linkFor(best.el.id, best.options) : null;
|
|
78
77
|
const active = _ScrollSpy._active.get(resolver);
|
|
78
|
+
if (best && active === best && best._link?.isConnected) return;
|
|
79
|
+
const next = best ? _ScrollSpy._linkFor(best.el.id, best.options) : null;
|
|
79
80
|
if (active === best && active?._link === next) return;
|
|
80
81
|
_ScrollSpy._clearActive(resolver);
|
|
81
82
|
if (!next) return;
|
|
@@ -132,4 +133,4 @@ var ScrollSpy = _ScrollSpy;
|
|
|
132
133
|
export {
|
|
133
134
|
ScrollSpy
|
|
134
135
|
};
|
|
135
|
-
//# sourceMappingURL=chunk-
|
|
136
|
+
//# sourceMappingURL=chunk-MZ6Z24BX.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/ts/components/scrollspy.ts"],
|
|
4
|
-
"sourcesContent": ["import { Component, BaseOptions, InitElements, InitElement } from '../core/component';\n\nexport interface ScrollSpyOptions extends BaseOptions {\n /**\n * Offset used as IntersectionObserver rootMargin and as\n * `--md-comp-scrollspy-offset` (scroll-margin on the section).\n * @default 200\n */\n scrollOffset: number;\n /**\n * Class applied to active elements.\n * @default 'active'\n */\n activeClass: string;\n /**\n * Returns a CSS selector for the TOC link. The default is unused: links\n * are found by comparing `a[href^=\"#\"]` to `#id`. A custom function\n * still runs through `querySelector` (escape any author id yourself).\n * @default id => 'a[href=\"#' + id + '\"]'\n */\n getActiveElement: (id: string) => string;\n /**\n * If true, keep the last section above the viewport active when nothing\n * intersects. If there is no such section, the first one stays active.\n * @default false\n */\n keepTopElementActive: boolean;\n}\n\nconst _defaultGetActiveElement = (id: string): string => `a[href=\"#${id}\"]`;\n\nconst _defaults: ScrollSpyOptions = {\n scrollOffset: 200,\n activeClass: 'active',\n getActiveElement: _defaultGetActiveElement,\n keepTopElementActive: false\n};\n\n/**\n * ScrollSpy. IntersectionObserver decides which section is current.\n * Hash links are left to the browser (`scroll-margin` + `scroll-behavior`).\n */\nexport class ScrollSpy extends Component<ScrollSpyOptions> {\n static _elements: ScrollSpy[] = [];\n private static _active = new Map<ScrollSpyOptions['getActiveElement'], ScrollSpy>();\n private _observer: IntersectionObserver;\n private _ratio = 0;\n private _link: Element | null = null;\n private _offset: string;\n private _offsetPriority: string;\n\n constructor(el: HTMLElement, options: Partial<ScrollSpyOptions>) {\n super(el, options, ScrollSpy);\n this.el['Expressive_ScrollSpy'] = this;\n\n this.options = {\n ...ScrollSpy.defaults,\n ...options\n };\n\n this._offset = this.el.style.getPropertyValue('--md-comp-scrollspy-offset');\n this._offsetPriority = this.el.style.getPropertyPriority('--md-comp-scrollspy-offset');\n this.el.style.setProperty(\n '--md-comp-scrollspy-offset',\n `${this.options.scrollOffset}px`\n );\n\n ScrollSpy._elements.push(this);\n if (typeof IntersectionObserver !== 'undefined') {\n this._observer = new IntersectionObserver((entries) => {\n if (ScrollSpy.getInstance(this.el) !== this) return;\n for (const entry of entries) this._ratio = entry.isIntersecting ? entry.intersectionRatio : 0;\n ScrollSpy._syncActive(this.options.getActiveElement);\n }, {\n rootMargin: `-${this.options.scrollOffset}px 0px -45% 0px`,\n threshold: [0, 0.1, 0.25, 0.5, 0.75, 1]\n });\n this._observer.observe(this.el);\n }\n }\n\n static get defaults(): ScrollSpyOptions {\n return _defaults;\n }\n\n static init(el: HTMLElement, options?: Partial<ScrollSpyOptions>): ScrollSpy;\n static init(els: InitElements<InitElement>, options?: Partial<ScrollSpyOptions>): ScrollSpy[];\n static init(\n els: HTMLElement | InitElements<InitElement>,\n options: Partial<ScrollSpyOptions> = {}\n ): ScrollSpy | ScrollSpy[] {\n return super.init(els, options, ScrollSpy);\n }\n\n static getInstance(el: HTMLElement): ScrollSpy {\n return el['Expressive_ScrollSpy'];\n }\n\n destroy() {\n const index = ScrollSpy._elements.indexOf(this);\n if (index < 0) return;\n ScrollSpy._elements.splice(index, 1);\n this._observer?.disconnect();\n const resolver = this.options.getActiveElement;\n if (ScrollSpy._active.get(resolver) === this) ScrollSpy._clearActive(resolver);\n this.el.style.setProperty('--md-comp-scrollspy-offset', this._offset, this._offsetPriority);\n this.el['Expressive_ScrollSpy'] = undefined;\n ScrollSpy._syncActive(resolver);\n }\n\n private static _syncActive(resolver: ScrollSpyOptions['getActiveElement']) {\n const elements = ScrollSpy._elements.filter(spy => spy.options.getActiveElement === resolver);\n\n let best: ScrollSpy | null = null;\n let bestRatio = 0;\n for (const spy of elements) {\n const ratio = spy._ratio;\n if (ratio > bestRatio) {\n bestRatio = ratio;\n best = spy;\n }\n }\n\n if (!best) {\n const retained = elements.filter(spy => spy.options.keepTopElementActive);\n best = ScrollSpy._nearestAbove(retained) ?? retained[0];\n }\n\n const next = best ? ScrollSpy._linkFor(best.el.id, best.options) : null;\n
|
|
5
|
-
"mappings": ";;;;;AA6BA,IAAM,2BAA2B,CAAC,OAAuB,YAAY,EAAE;AAEvE,IAAM,YAA8B;AAAA,EAClC,cAAc;AAAA,EACd,aAAa;AAAA,EACb,kBAAkB;AAAA,EAClB,sBAAsB;AACxB;AAMO,IAAM,aAAN,MAAM,mBAAkB,UAA4B;AAAA,EASzD,YAAY,IAAiB,SAAoC;AAC/D,UAAM,IAAI,SAAS,UAAS;AAN9B,SAAQ,SAAS;AACjB,SAAQ,QAAwB;AAM9B,SAAK,GAAG,sBAAsB,IAAI;AAElC,SAAK,UAAU;AAAA,MACb,GAAG,WAAU;AAAA,MACb,GAAG;AAAA,IACL;AAEA,SAAK,UAAU,KAAK,GAAG,MAAM,iBAAiB,4BAA4B;AAC1E,SAAK,kBAAkB,KAAK,GAAG,MAAM,oBAAoB,4BAA4B;AACrF,SAAK,GAAG,MAAM;AAAA,MACZ;AAAA,MACA,GAAG,KAAK,QAAQ,YAAY;AAAA,IAC9B;AAEA,eAAU,UAAU,KAAK,IAAI;AAC7B,QAAI,OAAO,yBAAyB,aAAa;AAC/C,WAAK,YAAY,IAAI,qBAAqB,CAAC,YAAY;AACrD,YAAI,WAAU,YAAY,KAAK,EAAE,MAAM,KAAM;AAC7C,mBAAW,SAAS,QAAS,MAAK,SAAS,MAAM,iBAAiB,MAAM,oBAAoB;AAC5F,mBAAU,YAAY,KAAK,QAAQ,gBAAgB;AAAA,MACrD,GAAG;AAAA,QACD,YAAY,IAAI,KAAK,QAAQ,YAAY;AAAA,QACzC,WAAW,CAAC,GAAG,KAAK,MAAM,KAAK,MAAM,CAAC;AAAA,MACxC,CAAC;AACD,WAAK,UAAU,QAAQ,KAAK,EAAE;AAAA,IAChC;AAAA,EACF;AAAA,EAEA,WAAW,WAA6B;AACtC,WAAO;AAAA,EACT;AAAA,EAIA,OAAO,KACL,KACA,UAAqC,CAAC,GACb;AACzB,WAAO,MAAM,KAAK,KAAK,SAAS,UAAS;AAAA,EAC3C;AAAA,EAEA,OAAO,YAAY,IAA4B;AAC7C,WAAO,GAAG,sBAAsB;AAAA,EAClC;AAAA,EAEA,UAAU;AACR,UAAM,QAAQ,WAAU,UAAU,QAAQ,IAAI;AAC9C,QAAI,QAAQ,EAAG;AACf,eAAU,UAAU,OAAO,OAAO,CAAC;AACnC,SAAK,WAAW,WAAW;AAC3B,UAAM,WAAW,KAAK,QAAQ;AAC9B,QAAI,WAAU,QAAQ,IAAI,QAAQ,MAAM,KAAM,YAAU,aAAa,QAAQ;AAC7E,SAAK,GAAG,MAAM,YAAY,8BAA8B,KAAK,SAAS,KAAK,eAAe;AAC1F,SAAK,GAAG,sBAAsB,IAAI;AAClC,eAAU,YAAY,QAAQ;AAAA,EAChC;AAAA,EAEA,OAAe,YAAY,UAAgD;AACzE,UAAM,WAAW,WAAU,UAAU,OAAO,SAAO,IAAI,QAAQ,qBAAqB,QAAQ;AAE5F,QAAI,OAAyB;AAC7B,QAAI,YAAY;AAChB,eAAW,OAAO,UAAU;AAC1B,YAAM,QAAQ,IAAI;AAClB,UAAI,QAAQ,WAAW;AACrB,oBAAY;AACZ,eAAO;AAAA,MACT;AAAA,IACF;AAEA,QAAI,CAAC,MAAM;AACT,YAAM,WAAW,SAAS,OAAO,SAAO,IAAI,QAAQ,oBAAoB;AACxE,aAAO,WAAU,cAAc,QAAQ,KAAK,SAAS,CAAC;AAAA,IACxD;AAEA,UAAM,
|
|
4
|
+
"sourcesContent": ["import { Component, BaseOptions, InitElements, InitElement } from '../core/component';\n\nexport interface ScrollSpyOptions extends BaseOptions {\n /**\n * Offset used as IntersectionObserver rootMargin and as\n * `--md-comp-scrollspy-offset` (scroll-margin on the section).\n * @default 200\n */\n scrollOffset: number;\n /**\n * Class applied to active elements.\n * @default 'active'\n */\n activeClass: string;\n /**\n * Returns a CSS selector for the TOC link. The default is unused: links\n * are found by comparing `a[href^=\"#\"]` to `#id`. A custom function\n * still runs through `querySelector` (escape any author id yourself).\n * @default id => 'a[href=\"#' + id + '\"]'\n */\n getActiveElement: (id: string) => string;\n /**\n * If true, keep the last section above the viewport active when nothing\n * intersects. If there is no such section, the first one stays active.\n * @default false\n */\n keepTopElementActive: boolean;\n}\n\nconst _defaultGetActiveElement = (id: string): string => `a[href=\"#${id}\"]`;\n\nconst _defaults: ScrollSpyOptions = {\n scrollOffset: 200,\n activeClass: 'active',\n getActiveElement: _defaultGetActiveElement,\n keepTopElementActive: false\n};\n\n/**\n * ScrollSpy. IntersectionObserver decides which section is current.\n * Hash links are left to the browser (`scroll-margin` + `scroll-behavior`).\n */\nexport class ScrollSpy extends Component<ScrollSpyOptions> {\n static _elements: ScrollSpy[] = [];\n private static _active = new Map<ScrollSpyOptions['getActiveElement'], ScrollSpy>();\n private _observer: IntersectionObserver;\n private _ratio = 0;\n private _link: Element | null = null;\n private _offset: string;\n private _offsetPriority: string;\n\n constructor(el: HTMLElement, options: Partial<ScrollSpyOptions>) {\n super(el, options, ScrollSpy);\n this.el['Expressive_ScrollSpy'] = this;\n\n this.options = {\n ...ScrollSpy.defaults,\n ...options\n };\n\n this._offset = this.el.style.getPropertyValue('--md-comp-scrollspy-offset');\n this._offsetPriority = this.el.style.getPropertyPriority('--md-comp-scrollspy-offset');\n this.el.style.setProperty(\n '--md-comp-scrollspy-offset',\n `${this.options.scrollOffset}px`\n );\n\n ScrollSpy._elements.push(this);\n if (typeof IntersectionObserver !== 'undefined') {\n this._observer = new IntersectionObserver((entries) => {\n if (ScrollSpy.getInstance(this.el) !== this) return;\n for (const entry of entries) this._ratio = entry.isIntersecting ? entry.intersectionRatio : 0;\n ScrollSpy._syncActive(this.options.getActiveElement);\n }, {\n rootMargin: `-${this.options.scrollOffset}px 0px -45% 0px`,\n threshold: [0, 0.1, 0.25, 0.5, 0.75, 1]\n });\n this._observer.observe(this.el);\n }\n }\n\n static get defaults(): ScrollSpyOptions {\n return _defaults;\n }\n\n static init(el: HTMLElement, options?: Partial<ScrollSpyOptions>): ScrollSpy;\n static init(els: InitElements<InitElement>, options?: Partial<ScrollSpyOptions>): ScrollSpy[];\n static init(\n els: HTMLElement | InitElements<InitElement>,\n options: Partial<ScrollSpyOptions> = {}\n ): ScrollSpy | ScrollSpy[] {\n return super.init(els, options, ScrollSpy);\n }\n\n static getInstance(el: HTMLElement): ScrollSpy {\n return el['Expressive_ScrollSpy'];\n }\n\n destroy() {\n const index = ScrollSpy._elements.indexOf(this);\n if (index < 0) return;\n ScrollSpy._elements.splice(index, 1);\n this._observer?.disconnect();\n const resolver = this.options.getActiveElement;\n if (ScrollSpy._active.get(resolver) === this) ScrollSpy._clearActive(resolver);\n this.el.style.setProperty('--md-comp-scrollspy-offset', this._offset, this._offsetPriority);\n this.el['Expressive_ScrollSpy'] = undefined;\n ScrollSpy._syncActive(resolver);\n }\n\n private static _syncActive(resolver: ScrollSpyOptions['getActiveElement']) {\n const elements = ScrollSpy._elements.filter(spy => spy.options.getActiveElement === resolver);\n\n let best: ScrollSpy | null = null;\n let bestRatio = 0;\n for (const spy of elements) {\n const ratio = spy._ratio;\n if (ratio > bestRatio) {\n bestRatio = ratio;\n best = spy;\n }\n }\n\n if (!best) {\n const retained = elements.filter(spy => spy.options.keepTopElementActive);\n best = ScrollSpy._nearestAbove(retained) ?? retained[0];\n }\n\n const active = ScrollSpy._active.get(resolver);\n // Most callbacks only move a ratio within the same section. Its link is\n // already marked, so skip the document-wide lookup.\n if (best && active === best && best._link?.isConnected) return;\n const next = best ? ScrollSpy._linkFor(best.el.id, best.options) : null;\n if (active === best && active?._link === next) return;\n ScrollSpy._clearActive(resolver);\n if (!next) return;\n next.classList.add(best.options.activeClass);\n next.setAttribute('aria-current', 'true');\n best._link = next;\n ScrollSpy._active.set(resolver, best);\n }\n\n private static _nearestAbove(elements: ScrollSpy[]): ScrollSpy | null {\n let best: ScrollSpy | null = null;\n let bestTop = -Infinity;\n for (const spy of elements) {\n const top = spy.el.getBoundingClientRect().top;\n if (top <= 0 && top >= bestTop) {\n bestTop = top;\n best = spy;\n }\n }\n return best;\n }\n\n private static _clearActive(resolver: ScrollSpyOptions['getActiveElement']) {\n const active = ScrollSpy._active.get(resolver);\n if (!active) return;\n active._link?.classList.remove(active.options.activeClass);\n active._link?.removeAttribute('aria-current');\n active._link = null;\n ScrollSpy._active.delete(resolver);\n }\n\n /**\n * Default lookup compares href attributes (no interpolated selector).\n * A custom getActiveElement still returns a selector.\n */\n static _linkFor(id: string, options: ScrollSpyOptions): Element | null {\n if (!id) return null;\n if (options.getActiveElement !== _defaultGetActiveElement) {\n try {\n return document.querySelector(options.getActiveElement(id));\n } catch {\n return null;\n }\n }\n const href = `#${id}`;\n const links = document.querySelectorAll('a[href^=\"#\"]');\n for (let i = 0; i < links.length; i++) {\n if (links[i].getAttribute('href') === href) return links[i];\n }\n return null;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Utils
|
|
3
|
+
} from "./chunk-MNPROCZF.mjs";
|
|
4
|
+
import {
|
|
5
|
+
Component
|
|
6
|
+
} from "./chunk-LTWPGFBR.mjs";
|
|
7
|
+
|
|
8
|
+
// src/ts/components/messageScroller.ts
|
|
9
|
+
var _defaults = {
|
|
10
|
+
autoScroll: true,
|
|
11
|
+
defaultScrollPosition: "end",
|
|
12
|
+
peek: 64
|
|
13
|
+
};
|
|
14
|
+
var EDGE = 2;
|
|
15
|
+
var UP_KEYS = /* @__PURE__ */ new Set(["ArrowUp", "PageUp", "Home"]);
|
|
16
|
+
var RELEASE_EVENTS = ["pointerup", "pointercancel", "touchend", "touchcancel"];
|
|
17
|
+
var MessageScroller = class _MessageScroller extends Component {
|
|
18
|
+
constructor(el, options) {
|
|
19
|
+
super(el, options, _MessageScroller);
|
|
20
|
+
this._opened = false;
|
|
21
|
+
this._following = false;
|
|
22
|
+
/** A press is down: it may be a tap or a scrollbar drag. */
|
|
23
|
+
this._holding = false;
|
|
24
|
+
this._lastTop = 0;
|
|
25
|
+
/** Where a programmatic scroll is heading; null when none is running. */
|
|
26
|
+
this._target = null;
|
|
27
|
+
/** Distance left to the target at the last scroll event. */
|
|
28
|
+
this._distance = Infinity;
|
|
29
|
+
/** The first visible message and its offset from the viewport top. */
|
|
30
|
+
this._ref = null;
|
|
31
|
+
this._refOffset = 0;
|
|
32
|
+
/** The latest anchored turn, which the spacer lets reach the top. */
|
|
33
|
+
this._anchor = null;
|
|
34
|
+
this._spacer = 0;
|
|
35
|
+
this._last = null;
|
|
36
|
+
this._resizeObserver = null;
|
|
37
|
+
this._mutationObserver = null;
|
|
38
|
+
// A programmatic scroll ends when it arrives, or when a scroll moves away
|
|
39
|
+
// from its target because something else took over.
|
|
40
|
+
this._handleScroll = () => {
|
|
41
|
+
if (this._target !== null) {
|
|
42
|
+
const distance = Math.abs(this.viewport.scrollTop - this._resolve(this._target));
|
|
43
|
+
if (distance > EDGE && distance < this._distance) {
|
|
44
|
+
this._distance = distance;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
this._target = null;
|
|
48
|
+
}
|
|
49
|
+
this._settle();
|
|
50
|
+
};
|
|
51
|
+
this._handleUserScroll = (e) => {
|
|
52
|
+
if (e.type === "wheel" && e.deltaY >= 0) return;
|
|
53
|
+
if (e.type === "keydown" && !UP_KEYS.has(e.key)) return;
|
|
54
|
+
this._target = null;
|
|
55
|
+
if (e.type === "pointerdown" || e.type === "touchstart") this._holding = true;
|
|
56
|
+
else this._following = false;
|
|
57
|
+
};
|
|
58
|
+
this._handleRelease = () => {
|
|
59
|
+
if (!this._holding) return;
|
|
60
|
+
this._holding = false;
|
|
61
|
+
if (this._following) this.viewport.scrollTop = this.viewport.scrollHeight;
|
|
62
|
+
this._sync();
|
|
63
|
+
};
|
|
64
|
+
this._handleButtonClick = () => this.scrollToEnd();
|
|
65
|
+
this._handleMutations = (records) => {
|
|
66
|
+
for (const record of records) {
|
|
67
|
+
record.addedNodes.forEach((node) => {
|
|
68
|
+
if (node instanceof Element) this._resizeObserver?.observe(node);
|
|
69
|
+
});
|
|
70
|
+
record.removedNodes.forEach((node) => {
|
|
71
|
+
if (node instanceof Element) this._resizeObserver?.unobserve(node);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const previous = this._last;
|
|
75
|
+
this._last = this.viewport.lastElementChild;
|
|
76
|
+
if (!this._opened) return;
|
|
77
|
+
if (!this._following && this._target === null) this._restore();
|
|
78
|
+
if (!this.options.autoScroll) return;
|
|
79
|
+
if (previous && previous.parentElement !== this.viewport) return;
|
|
80
|
+
for (let item = this._last; item && item !== previous; item = item.previousElementSibling) {
|
|
81
|
+
if (item.hasAttribute("data-scroll-anchor")) {
|
|
82
|
+
this._anchor = item;
|
|
83
|
+
this._updateSpacer();
|
|
84
|
+
this._following = true;
|
|
85
|
+
this._scrollTo(this._top(item) - this.options.peek);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
this._handleResize = () => {
|
|
91
|
+
if (!this._opened) {
|
|
92
|
+
if (this.viewport.clientHeight > 0) this._open();
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
this._updateSpacer();
|
|
96
|
+
const v = this.viewport;
|
|
97
|
+
if (this._target === "end") this._scrollTo("end");
|
|
98
|
+
else if (this._target === null) {
|
|
99
|
+
if (this._following && !this._holding) v.scrollTop = v.scrollHeight;
|
|
100
|
+
else this._restore();
|
|
101
|
+
}
|
|
102
|
+
this._sync();
|
|
103
|
+
};
|
|
104
|
+
this.el["Expressive_MessageScroller"] = this;
|
|
105
|
+
this.options = {
|
|
106
|
+
..._MessageScroller.defaults,
|
|
107
|
+
...options
|
|
108
|
+
};
|
|
109
|
+
this.viewport = el.querySelector(":scope > .message-scroller-viewport");
|
|
110
|
+
this.button = el.querySelector(":scope > .message-scroller-button");
|
|
111
|
+
if (!this.viewport) {
|
|
112
|
+
console.error("MessageScroller needs a .message-scroller-viewport child.");
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
this._last = this.viewport.lastElementChild;
|
|
116
|
+
if (this.viewport.clientHeight > 0) this._open();
|
|
117
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
118
|
+
this._resizeObserver = new ResizeObserver(this._handleResize);
|
|
119
|
+
this._resizeObserver.observe(this.viewport, { box: "border-box" });
|
|
120
|
+
for (const item of Array.from(this.viewport.children)) this._resizeObserver.observe(item);
|
|
121
|
+
}
|
|
122
|
+
this._mutationObserver = new MutationObserver(this._handleMutations);
|
|
123
|
+
this._mutationObserver.observe(this.viewport, { childList: true });
|
|
124
|
+
this.viewport.addEventListener("scroll", this._handleScroll, { passive: true });
|
|
125
|
+
for (const type of ["wheel", "touchstart", "pointerdown", "keydown"]) {
|
|
126
|
+
this.viewport.addEventListener(type, this._handleUserScroll, { passive: true });
|
|
127
|
+
}
|
|
128
|
+
for (const type of RELEASE_EVENTS) document.addEventListener(type, this._handleRelease);
|
|
129
|
+
this.button?.addEventListener("click", this._handleButtonClick);
|
|
130
|
+
}
|
|
131
|
+
static get defaults() {
|
|
132
|
+
return _defaults;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Initializes instances of MessageScroller.
|
|
136
|
+
* @param els HTML elements.
|
|
137
|
+
* @param options Component options.
|
|
138
|
+
*/
|
|
139
|
+
static init(els, options = {}) {
|
|
140
|
+
return super.init(els, options, _MessageScroller);
|
|
141
|
+
}
|
|
142
|
+
static getInstance(el) {
|
|
143
|
+
return el["Expressive_MessageScroller"];
|
|
144
|
+
}
|
|
145
|
+
destroy() {
|
|
146
|
+
this.el["Expressive_MessageScroller"] = void 0;
|
|
147
|
+
if (!this.viewport) return;
|
|
148
|
+
this._resizeObserver?.disconnect();
|
|
149
|
+
this._mutationObserver?.disconnect();
|
|
150
|
+
this.viewport.removeEventListener("scroll", this._handleScroll);
|
|
151
|
+
for (const type of ["wheel", "touchstart", "pointerdown", "keydown"]) {
|
|
152
|
+
this.viewport.removeEventListener(type, this._handleUserScroll);
|
|
153
|
+
}
|
|
154
|
+
for (const type of RELEASE_EVENTS) document.removeEventListener(type, this._handleRelease);
|
|
155
|
+
this.viewport.style.removeProperty("--md-comp-message-scroller-spacer");
|
|
156
|
+
if (this.button) {
|
|
157
|
+
this.button.removeEventListener("click", this._handleButtonClick);
|
|
158
|
+
this.button.inert = false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/** Scrolls to the latest message and resumes following. */
|
|
162
|
+
scrollToEnd() {
|
|
163
|
+
this._following = this.options.autoScroll;
|
|
164
|
+
this._scrollTo("end");
|
|
165
|
+
}
|
|
166
|
+
/** Scrolls to the first message. */
|
|
167
|
+
scrollToStart() {
|
|
168
|
+
this._following = false;
|
|
169
|
+
this._scrollTo(0);
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Scrolls the element with this id to the top of the viewport, below the
|
|
173
|
+
* peek, and stops following.
|
|
174
|
+
* @returns false when no such element is inside the viewport.
|
|
175
|
+
*/
|
|
176
|
+
scrollToMessage(id) {
|
|
177
|
+
const target = Utils.getElementById(this.viewport, id);
|
|
178
|
+
if (!target || !this.viewport.contains(target)) return false;
|
|
179
|
+
this._following = false;
|
|
180
|
+
this._scrollTo(this._top(target) - this.options.peek);
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
_open() {
|
|
184
|
+
this._opened = true;
|
|
185
|
+
const v = this.viewport;
|
|
186
|
+
const position = this.options.defaultScrollPosition;
|
|
187
|
+
const anchors = v.querySelectorAll(":scope > [data-scroll-anchor]");
|
|
188
|
+
const anchor = position === "last-anchor" ? anchors[anchors.length - 1] : null;
|
|
189
|
+
if (position === "start") v.scrollTop = 0;
|
|
190
|
+
else if (anchor && this._contentEnd() - this._top(anchor) + this.options.peek > v.clientHeight) {
|
|
191
|
+
v.scrollTop = this._top(anchor) - this.options.peek;
|
|
192
|
+
} else v.scrollTop = v.scrollHeight;
|
|
193
|
+
this._settle();
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Records where the reader is once no programmatic scroll is running.
|
|
197
|
+
* Content can grow between a scroll and the resize that re-pins the end,
|
|
198
|
+
* so only moving toward the start releases following.
|
|
199
|
+
*/
|
|
200
|
+
_settle() {
|
|
201
|
+
const top = this.viewport.scrollTop;
|
|
202
|
+
const kept = this._following && top >= this._lastTop - EDGE;
|
|
203
|
+
this._following = this.options.autoScroll && (kept || this._atEnd());
|
|
204
|
+
this._lastTop = top;
|
|
205
|
+
this._capture();
|
|
206
|
+
this._sync();
|
|
207
|
+
}
|
|
208
|
+
_scrollTo(target) {
|
|
209
|
+
const v = this.viewport;
|
|
210
|
+
if (Math.abs(v.scrollTop - this._resolve(target)) <= EDGE) {
|
|
211
|
+
this._target = null;
|
|
212
|
+
this._settle();
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
this._target = target;
|
|
216
|
+
this._distance = Infinity;
|
|
217
|
+
const reduce = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
218
|
+
v.scrollTo({
|
|
219
|
+
top: target === "end" ? v.scrollHeight : target,
|
|
220
|
+
behavior: reduce ? "auto" : "smooth"
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
_resolve(target) {
|
|
224
|
+
const max = this.viewport.scrollHeight - this.viewport.clientHeight;
|
|
225
|
+
return target === "end" ? max : Math.max(0, Math.min(target, max));
|
|
226
|
+
}
|
|
227
|
+
_atEnd() {
|
|
228
|
+
const v = this.viewport;
|
|
229
|
+
return v.scrollHeight - v.scrollTop - v.clientHeight <= EDGE;
|
|
230
|
+
}
|
|
231
|
+
/** Offset of an element from the top of the scrolled content. */
|
|
232
|
+
_top(el) {
|
|
233
|
+
const v = this.viewport;
|
|
234
|
+
return el.getBoundingClientRect().top - v.getBoundingClientRect().top - v.clientTop + v.scrollTop;
|
|
235
|
+
}
|
|
236
|
+
/** Bottom of the content without the spacer, padding included. */
|
|
237
|
+
_contentEnd() {
|
|
238
|
+
const last = this.viewport.lastElementChild;
|
|
239
|
+
if (!last) return 0;
|
|
240
|
+
const padding = parseFloat(getComputedStyle(this.viewport).paddingBlockEnd) - this._spacer;
|
|
241
|
+
return this._top(last) + last.getBoundingClientRect().height + padding;
|
|
242
|
+
}
|
|
243
|
+
/** Remembers the first visible message so prepends can keep it in place. */
|
|
244
|
+
_capture() {
|
|
245
|
+
const items = this.viewport.children;
|
|
246
|
+
const top = this.viewport.getBoundingClientRect().top + this.viewport.clientTop;
|
|
247
|
+
let low = 0;
|
|
248
|
+
let high = items.length - 1;
|
|
249
|
+
while (low < high) {
|
|
250
|
+
const mid = low + high >> 1;
|
|
251
|
+
if (items[mid].getBoundingClientRect().bottom > top) high = mid;
|
|
252
|
+
else low = mid + 1;
|
|
253
|
+
}
|
|
254
|
+
this._ref = items[low] ?? null;
|
|
255
|
+
this._refOffset = this._ref ? this._ref.getBoundingClientRect().top - top : 0;
|
|
256
|
+
}
|
|
257
|
+
/** Moves the remembered message back to where the reader last saw it. */
|
|
258
|
+
_restore() {
|
|
259
|
+
if (this._ref?.parentElement !== this.viewport) {
|
|
260
|
+
this._capture();
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const top = this.viewport.getBoundingClientRect().top + this.viewport.clientTop;
|
|
264
|
+
const delta = this._ref.getBoundingClientRect().top - top - this._refOffset;
|
|
265
|
+
if (Math.abs(delta) >= 1) this.viewport.scrollTop += delta;
|
|
266
|
+
}
|
|
267
|
+
/** Sizes the blank space that lets the latest anchored turn reach the top. */
|
|
268
|
+
_updateSpacer() {
|
|
269
|
+
let space = 0;
|
|
270
|
+
if (this._anchor?.parentElement === this.viewport) {
|
|
271
|
+
const room = this._top(this._anchor) - this.options.peek + this.viewport.clientHeight;
|
|
272
|
+
space = Math.max(0, Math.ceil(room - this._contentEnd()));
|
|
273
|
+
} else this._anchor = null;
|
|
274
|
+
if (space === this._spacer) return;
|
|
275
|
+
this._spacer = space;
|
|
276
|
+
if (space) this.viewport.style.setProperty("--md-comp-message-scroller-spacer", `${space}px`);
|
|
277
|
+
else this.viewport.style.removeProperty("--md-comp-message-scroller-spacer");
|
|
278
|
+
}
|
|
279
|
+
/** The button is inert while there is nothing below. */
|
|
280
|
+
_sync() {
|
|
281
|
+
if (!this.button) return;
|
|
282
|
+
const idle = this._atEnd();
|
|
283
|
+
const root = this.button.getRootNode();
|
|
284
|
+
if (idle && root.activeElement === this.button) this.viewport.focus({ preventScroll: true });
|
|
285
|
+
this.button.inert = idle;
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
export {
|
|
290
|
+
MessageScroller
|
|
291
|
+
};
|
|
292
|
+
//# sourceMappingURL=chunk-NS2UMNZV.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/ts/components/messageScroller.ts"],
|
|
4
|
+
"sourcesContent": ["import { Utils } from '../core/utils';\nimport { Component, BaseOptions, InitElements, InitElement } from '../core/component';\n\nexport interface MessageScrollerOptions extends BaseOptions {\n /**\n * If true, the viewport follows new content while the reader is at the\n * end, and a newly appended scroll anchor is brought to the top.\n * @default true\n */\n autoScroll: boolean;\n /**\n * Where the transcript opens: `'end'`, `'start'`, or `'last-anchor'`, the\n * start of the last anchored turn. `'last-anchor'` opens at the end when\n * that turn already fits in the viewport.\n * @default 'end'\n */\n defaultScrollPosition: 'start' | 'end' | 'last-anchor';\n /**\n * Pixels of the previous message left visible above an anchored turn or a\n * message reached with `scrollToMessage()`.\n * @default 64\n */\n peek: number;\n}\n\nconst _defaults: MessageScrollerOptions = {\n autoScroll: true,\n defaultScrollPosition: 'end',\n peek: 64\n};\n\n// Distance from a scroll target, in px, that still counts as reaching it.\n// Fractional layouts rarely land on the exact pixel.\nconst EDGE = 2;\n\n// Keys that scroll toward the start. They release following at once, before\n// the next streamed chunk can pull the viewport back to the end.\nconst UP_KEYS = new Set(['ArrowUp', 'PageUp', 'Home']);\n\n// Listened on the document: a scrollbar drag can end outside the viewport.\nconst RELEASE_EVENTS = ['pointerup', 'pointercancel', 'touchend', 'touchcancel'];\n\ntype Target = number | 'end';\n\n/**\n * A chat scroll container: a `.message-scroller` frame holding a scrolling\n * `.message-scroller-viewport` of messages and an optional\n * `.message-scroller-button`. Every child of the viewport is one message.\n *\n * While the reader is at the end, the viewport follows streamed output.\n * Scrolling toward the start releases it; reaching the end again, or the\n * button, resumes it. An appended message marked `data-scroll-anchor` starts a\n * turn and is scrolled to the top, with blank space below it until the reply\n * fills the viewport. Messages added above keep the reader's place.\n */\nexport class MessageScroller extends Component<MessageScrollerOptions> {\n /** The scrolling element. */\n viewport: HTMLElement;\n /** The scroll-to-latest button, if present. */\n button: HTMLButtonElement | null;\n private _opened = false;\n private _following = false;\n /** A press is down: it may be a tap or a scrollbar drag. */\n private _holding = false;\n private _lastTop = 0;\n /** Where a programmatic scroll is heading; null when none is running. */\n private _target: Target | null = null;\n /** Distance left to the target at the last scroll event. */\n private _distance = Infinity;\n /** The first visible message and its offset from the viewport top. */\n private _ref: Element | null = null;\n private _refOffset = 0;\n /** The latest anchored turn, which the spacer lets reach the top. */\n private _anchor: Element | null = null;\n private _spacer = 0;\n private _last: Element | null = null;\n private _resizeObserver: ResizeObserver | null = null;\n private _mutationObserver: MutationObserver | null = null;\n\n constructor(el: HTMLElement, options: Partial<MessageScrollerOptions>) {\n super(el, options, MessageScroller);\n this.el['Expressive_MessageScroller'] = this;\n this.options = {\n ...MessageScroller.defaults,\n ...options\n };\n this.viewport = el.querySelector(':scope > .message-scroller-viewport');\n this.button = el.querySelector(':scope > .message-scroller-button');\n if (!this.viewport) {\n console.error('MessageScroller needs a .message-scroller-viewport child.');\n return;\n }\n this._last = this.viewport.lastElementChild;\n // A viewport that is not rendered yet (a closed dialog, a hidden tab)\n // has no size to position against; it opens on its first resize.\n if (this.viewport.clientHeight > 0) this._open();\n if (typeof ResizeObserver !== 'undefined') {\n this._resizeObserver = new ResizeObserver(this._handleResize);\n // Border box: the spacer changes the padding, which must not count.\n this._resizeObserver.observe(this.viewport, { box: 'border-box' });\n for (const item of Array.from(this.viewport.children)) this._resizeObserver.observe(item);\n }\n this._mutationObserver = new MutationObserver(this._handleMutations);\n this._mutationObserver.observe(this.viewport, { childList: true });\n this.viewport.addEventListener('scroll', this._handleScroll, { passive: true });\n for (const type of ['wheel', 'touchstart', 'pointerdown', 'keydown']) {\n this.viewport.addEventListener(type, this._handleUserScroll, { passive: true });\n }\n for (const type of RELEASE_EVENTS) document.addEventListener(type, this._handleRelease);\n this.button?.addEventListener('click', this._handleButtonClick);\n }\n\n static get defaults(): MessageScrollerOptions {\n return _defaults;\n }\n\n /**\n * Initializes instance of MessageScroller.\n * @param el HTML element.\n * @param options Component options.\n */\n static init(el: HTMLElement, options?: Partial<MessageScrollerOptions>): MessageScroller;\n /**\n * Initializes instances of MessageScroller.\n * @param els HTML elements.\n * @param options Component options.\n */\n static init(\n els: InitElements<InitElement>,\n options?: Partial<MessageScrollerOptions>\n ): MessageScroller[];\n /**\n * Initializes instances of MessageScroller.\n * @param els HTML elements.\n * @param options Component options.\n */\n static init(\n els: HTMLElement | InitElements<InitElement>,\n options: Partial<MessageScrollerOptions> = {}\n ): MessageScroller | MessageScroller[] {\n return super.init(els, options, MessageScroller);\n }\n\n static getInstance(el: HTMLElement): MessageScroller {\n return el['Expressive_MessageScroller'];\n }\n\n destroy() {\n this.el['Expressive_MessageScroller'] = undefined;\n if (!this.viewport) return;\n this._resizeObserver?.disconnect();\n this._mutationObserver?.disconnect();\n this.viewport.removeEventListener('scroll', this._handleScroll);\n for (const type of ['wheel', 'touchstart', 'pointerdown', 'keydown']) {\n this.viewport.removeEventListener(type, this._handleUserScroll);\n }\n for (const type of RELEASE_EVENTS) document.removeEventListener(type, this._handleRelease);\n this.viewport.style.removeProperty('--md-comp-message-scroller-spacer');\n if (this.button) {\n this.button.removeEventListener('click', this._handleButtonClick);\n this.button.inert = false;\n }\n }\n\n /** Scrolls to the latest message and resumes following. */\n scrollToEnd() {\n // Set now: output streaming faster than the scroll can end it early.\n this._following = this.options.autoScroll;\n this._scrollTo('end');\n }\n\n /** Scrolls to the first message. */\n scrollToStart() {\n this._following = false;\n this._scrollTo(0);\n }\n\n /**\n * Scrolls the element with this id to the top of the viewport, below the\n * peek, and stops following.\n * @returns false when no such element is inside the viewport.\n */\n scrollToMessage(id: string): boolean {\n const target = Utils.getElementById(this.viewport, id);\n if (!target || !this.viewport.contains(target)) return false;\n this._following = false;\n this._scrollTo(this._top(target) - this.options.peek);\n return true;\n }\n\n private _open() {\n this._opened = true;\n const v = this.viewport;\n const position = this.options.defaultScrollPosition;\n const anchors = v.querySelectorAll(':scope > [data-scroll-anchor]');\n const anchor = position === 'last-anchor' ? anchors[anchors.length - 1] : null;\n if (position === 'start') v.scrollTop = 0;\n else if (anchor && this._contentEnd() - this._top(anchor) + this.options.peek > v.clientHeight) {\n v.scrollTop = this._top(anchor) - this.options.peek;\n } else v.scrollTop = v.scrollHeight;\n this._settle();\n }\n\n /**\n * Records where the reader is once no programmatic scroll is running.\n * Content can grow between a scroll and the resize that re-pins the end,\n * so only moving toward the start releases following.\n */\n private _settle() {\n const top = this.viewport.scrollTop;\n const kept = this._following && top >= this._lastTop - EDGE;\n this._following = this.options.autoScroll && (kept || this._atEnd());\n this._lastTop = top;\n this._capture();\n this._sync();\n }\n\n // A programmatic scroll ends when it arrives, or when a scroll moves away\n // from its target because something else took over.\n private _handleScroll = () => {\n if (this._target !== null) {\n const distance = Math.abs(this.viewport.scrollTop - this._resolve(this._target));\n if (distance > EDGE && distance < this._distance) {\n this._distance = distance;\n return;\n }\n this._target = null;\n }\n this._settle();\n };\n\n private _handleUserScroll = (e: Event) => {\n if (e.type === 'wheel' && (e as WheelEvent).deltaY >= 0) return;\n if (e.type === 'keydown' && !UP_KEYS.has((e as KeyboardEvent).key)) return;\n this._target = null;\n // A press may be a tap on a message or the start of a drag. Hold the end\n // until it is released; a scroll toward the start releases following.\n if (e.type === 'pointerdown' || e.type === 'touchstart') this._holding = true;\n else this._following = false;\n };\n\n private _handleRelease = () => {\n if (!this._holding) return;\n this._holding = false;\n if (this._following) this.viewport.scrollTop = this.viewport.scrollHeight;\n this._sync();\n };\n\n private _handleButtonClick = () => this.scrollToEnd();\n\n private _handleMutations = (records: MutationRecord[]) => {\n for (const record of records) {\n record.addedNodes.forEach((node) => {\n if (node instanceof Element) this._resizeObserver?.observe(node);\n });\n record.removedNodes.forEach((node) => {\n if (node instanceof Element) this._resizeObserver?.unobserve(node);\n });\n }\n const previous = this._last;\n this._last = this.viewport.lastElementChild;\n if (!this._opened) return;\n // Restore here, not only on resize: this runs before any scroll event\n // that could record the shifted position as the reader's place.\n if (!this._following && this._target === null) this._restore();\n if (!this.options.autoScroll) return;\n // Only messages appended after the previous last one start a new turn;\n // anchors in prepended history do not. If the last one was removed, the\n // list was replaced and nothing counts as appended.\n if (previous && previous.parentElement !== this.viewport) return;\n for (let item = this._last; item && item !== previous; item = item.previousElementSibling) {\n if (item.hasAttribute('data-scroll-anchor')) {\n this._anchor = item;\n this._updateSpacer();\n // Follow the reply once the turn reaches the top, unless the reader\n // scrolls away first. _settle keeps it because the scroll went down.\n this._following = true;\n this._scrollTo(this._top(item) - this.options.peek);\n return;\n }\n }\n };\n\n private _handleResize = () => {\n if (!this._opened) {\n if (this.viewport.clientHeight > 0) this._open();\n return;\n }\n this._updateSpacer();\n const v = this.viewport;\n if (this._target === 'end') this._scrollTo('end');\n else if (this._target === null) {\n if (this._following && !this._holding) v.scrollTop = v.scrollHeight;\n else this._restore();\n }\n this._sync();\n };\n\n private _scrollTo(target: Target) {\n const v = this.viewport;\n if (Math.abs(v.scrollTop - this._resolve(target)) <= EDGE) {\n this._target = null;\n this._settle();\n return;\n }\n this._target = target;\n this._distance = Infinity;\n const reduce = typeof matchMedia === 'function' && matchMedia('(prefers-reduced-motion: reduce)').matches;\n v.scrollTo({\n top: target === 'end' ? v.scrollHeight : target,\n behavior: reduce ? 'auto' : 'smooth'\n });\n }\n\n private _resolve(target: Target) {\n const max = this.viewport.scrollHeight - this.viewport.clientHeight;\n return target === 'end' ? max : Math.max(0, Math.min(target, max));\n }\n\n private _atEnd() {\n const v = this.viewport;\n return v.scrollHeight - v.scrollTop - v.clientHeight <= EDGE;\n }\n\n /** Offset of an element from the top of the scrolled content. */\n private _top(el: Element) {\n const v = this.viewport;\n return el.getBoundingClientRect().top - v.getBoundingClientRect().top - v.clientTop + v.scrollTop;\n }\n\n /** Bottom of the content without the spacer, padding included. */\n private _contentEnd() {\n const last = this.viewport.lastElementChild;\n if (!last) return 0;\n const padding = parseFloat(getComputedStyle(this.viewport).paddingBlockEnd) - this._spacer;\n return this._top(last) + last.getBoundingClientRect().height + padding;\n }\n\n /** Remembers the first visible message so prepends can keep it in place. */\n private _capture() {\n const items = this.viewport.children;\n const top = this.viewport.getBoundingClientRect().top + this.viewport.clientTop;\n let low = 0;\n let high = items.length - 1;\n while (low < high) {\n const mid = (low + high) >> 1;\n if (items[mid].getBoundingClientRect().bottom > top) high = mid;\n else low = mid + 1;\n }\n this._ref = items[low] ?? null;\n this._refOffset = this._ref ? this._ref.getBoundingClientRect().top - top : 0;\n }\n\n /** Moves the remembered message back to where the reader last saw it. */\n private _restore() {\n if (this._ref?.parentElement !== this.viewport) {\n this._capture();\n return;\n }\n const top = this.viewport.getBoundingClientRect().top + this.viewport.clientTop;\n const delta = this._ref.getBoundingClientRect().top - top - this._refOffset;\n if (Math.abs(delta) >= 1) this.viewport.scrollTop += delta;\n }\n\n /** Sizes the blank space that lets the latest anchored turn reach the top. */\n private _updateSpacer() {\n let space = 0;\n if (this._anchor?.parentElement === this.viewport) {\n const room = this._top(this._anchor) - this.options.peek + this.viewport.clientHeight;\n space = Math.max(0, Math.ceil(room - this._contentEnd()));\n } else this._anchor = null;\n if (space === this._spacer) return;\n this._spacer = space;\n if (space) this.viewport.style.setProperty('--md-comp-message-scroller-spacer', `${space}px`);\n else this.viewport.style.removeProperty('--md-comp-message-scroller-spacer');\n }\n\n /** The button is inert while there is nothing below. */\n private _sync() {\n if (!this.button) return;\n const idle = this._atEnd();\n // The button's own root: inside a shadow root, document.activeElement is the host.\n const root = this.button.getRootNode() as Document | ShadowRoot;\n if (idle && root.activeElement === this.button) this.viewport.focus({ preventScroll: true });\n this.button.inert = idle;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|