@brightspot/ui 3.0.1 → 5.0.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/README.md +56 -37
- package/dist/storybook/assets/{ActionBar.stories-BTkFDNZF.js → ActionBar.stories-IDc5Jeit.js} +1 -1
- package/dist/storybook/assets/{ActionItem.stories-BY1vOUMp.js → ActionItem.stories-FKbziuZR.js} +1 -1
- package/dist/storybook/assets/{Avatar.stories-BViF7cJM.js → Avatar.stories-BsAnn4F7.js} +1 -1
- package/dist/storybook/assets/{AvatarGroup.stories-DBLtdzWj.js → AvatarGroup.stories-BmNNGA4K.js} +1 -1
- package/dist/storybook/assets/{Badge.stories-BJzAykeL.js → Badge.stories-BMb_-0Io.js} +1 -1
- package/dist/storybook/assets/{Button-BS_DVZrR.js → Button-D3vjfsjz.js} +1 -1
- package/dist/storybook/assets/{Button.stories-CMa8tsGq.js → Button.stories-VndkwLDv.js} +1 -1
- package/dist/storybook/assets/{ButtonGroup.stories-Dorf10Q3.js → ButtonGroup.stories-BvbNykTx.js} +1 -1
- package/dist/storybook/assets/{Celebrate.stories-C0RndH03.js → Celebrate.stories-DrrL3txs.js} +1 -1
- package/dist/storybook/assets/{Checkbox.stories-DuM_lJ8J.js → Checkbox.stories-BS4H3-cO.js} +1 -1
- package/dist/storybook/assets/{CircularProgress.stories-Bv2S_R-k.js → CircularProgress.stories-YQfbjx-O.js} +1 -1
- package/dist/storybook/assets/{ClipboardMixin.stories-Bd1tO-g7.js → ClipboardMixin.stories-DjsApoGs.js} +1 -1
- package/dist/storybook/assets/{Color-6BZIO3FS-CKpudxYg.js → Color-6BZIO3FS-D23VaL29.js} +1 -1
- package/dist/storybook/assets/{Colors.stories-CLQ--V38.js → Colors.stories-B0Eo-AgP.js} +1 -1
- package/dist/storybook/assets/{CombinedEffects.stories-DnT8sQ2Z.js → CombinedEffects.stories-CJv4PhDZ.js} +1 -1
- package/dist/storybook/assets/{ComponentStatesMixin-Rv-1Sy61.js → ComponentStatesMixin-Bn8b1Qpq.js} +1 -1
- package/dist/storybook/assets/{ComponentStatesMixin.stories-DNs2OQFK.js → ComponentStatesMixin.stories-CM9Nn1Vr.js} +1 -1
- package/dist/storybook/assets/{CopyToClipboard.stories-Bz6tBGbk.js → CopyToClipboard.stories-DdwTxinQ.js} +1 -1
- package/dist/storybook/assets/{Debounce.stories-B2zRBNPi.js → Debounce.stories-bdqs8xGz.js} +1 -1
- package/dist/storybook/assets/{DocsRenderer-LL677BLK-DW8rd1PO.js → DocsRenderer-LL677BLK-CdjX43FI.js} +3 -3
- package/dist/storybook/assets/{Dropdown.stories-hnwE4FQe.js → Dropdown.stories-wY7jzy1b.js} +1 -1
- package/dist/storybook/assets/{EmptyState.stories-D8NwBW4d.js → EmptyState.stories-FpHAx68-.js} +1 -1
- package/dist/storybook/assets/{Events.stories-uC-17Dli.js → Events.stories-gJAakVdu.js} +1 -1
- package/dist/storybook/assets/{Heading.stories-gU4dU4SQ.js → Heading.stories-BqFtaYkf.js} +1 -1
- package/dist/storybook/assets/{HueRipple.stories-C9mL5X65.js → HueRipple.stories-Bk1g8tT8.js} +1 -1
- package/dist/storybook/assets/{Icon.stories-CODM5nby.js → Icon.stories-sxRLpE9W.js} +1 -1
- package/dist/storybook/assets/{IconButton.stories-B2ua_wfB.js → IconButton.stories-DjmbahEP.js} +1 -1
- package/dist/storybook/assets/{LinearProgress.stories-orcCd9Ym.js → LinearProgress.stories-DwJQwdLl.js} +1 -1
- package/dist/storybook/assets/{Pagination.stories-C4ygdgfN.js → Pagination.stories-Cp-E5mN-.js} +1 -1
- package/dist/storybook/assets/{Popover.stories-BCIIWmTA.js → Popover.stories-BRW3tYFh.js} +1 -1
- package/dist/storybook/assets/{ReadyMixin-Cxdzrrfi.js → ReadyMixin-Btxswj_k.js} +1 -1
- package/dist/storybook/assets/{RovingTabindexMixin.stories-DOwFdYhD.js → RovingTabindexMixin.stories-CU7heX4w.js} +1 -1
- package/dist/storybook/assets/{Rtc.stories-CHCfV1TF.js → Rtc.stories-CGkrXg7-.js} +1 -1
- package/dist/storybook/assets/{ScrollShadow.stories-BVa9OI5n.js → ScrollShadow.stories-HPqnieYi.js} +1 -1
- package/dist/storybook/assets/{Switch.stories-iU97zahz.js → Switch.stories-SIix50Dz.js} +1 -1
- package/dist/storybook/assets/{Tab.stories-Bibe-F62.js → Tab.stories-BJ0Dl--1.js} +1 -1
- package/dist/storybook/assets/{Tabs.stories-CF4_aHnK.js → Tabs.stories-DEV7dhqr.js} +1 -1
- package/dist/storybook/assets/{Throttle.stories-DIuUxjiJ.js → Throttle.stories-5l5dcEMx.js} +1 -1
- package/dist/storybook/assets/{Tooltip.stories-vob7VYrU.js → Tooltip.stories-CJrvBRM9.js} +1 -1
- package/dist/storybook/assets/{Upload.stories-2Tow92Sk.js → Upload.stories-HAndUW3D.js} +1 -1
- package/dist/storybook/assets/{UploadItem.stories-BDmgA2i-.js → UploadItem.stories-B-8IM6rL.js} +1 -1
- package/dist/storybook/assets/{Welcome.stories-CxDC5l_l.js → Welcome.stories-Cy7QZEXf.js} +1 -1
- package/dist/storybook/assets/{Widget.stories-Dj_P7LE4.js → Widget.stories-DyTOXrSZ.js} +1 -1
- package/dist/storybook/assets/{WithTooltip-65CFNBJE-C1mqF-R9.js → WithTooltip-65CFNBJE-BF-px3XD.js} +1 -1
- package/dist/storybook/assets/{blocks-CDIBgsPi.js → blocks-B-r6Fucc.js} +5 -5
- package/dist/storybook/assets/{formatter-EIJCOSYU-DlWS8Eh5.js → formatter-EIJCOSYU-BhTCWHea.js} +1 -1
- package/dist/storybook/assets/if-defined-CxLwFifW.js +1 -0
- package/dist/storybook/assets/{iframe-BgFj0b5u.css → iframe-D0roG0J-.css} +1 -1
- package/dist/storybook/assets/{iframe-B3sUtE0N.js → iframe-XJ17FJi7.js} +3 -3
- package/dist/storybook/assets/{index-CP948hPJ.js → index-CQ_bLab5.js} +1 -1
- package/dist/storybook/assets/{onFind-DtBCGkmC.js → onFind-RCD4BqgZ.js} +1 -1
- package/dist/storybook/assets/{onFind.stories-CdMaO-FL.js → onFind.stories-CD7vXuRn.js} +1 -1
- package/dist/storybook/assets/{onRemove.stories-CnNXun2Y.js → onRemove.stories-Dm8HrW-w.js} +1 -1
- package/dist/storybook/assets/{onVisible.stories-ZnFebS7S.js → onVisible.stories-CigtHPTK.js} +1 -1
- package/dist/storybook/assets/{style-map-Imekj22o.js → style-map-C3PJ2niZ.js} +1 -1
- package/dist/storybook/assets/{syntaxhighlighter-ED5Y7EFY-CqsAEB33.js → syntaxhighlighter-ED5Y7EFY-DIlzdeob.js} +1 -1
- package/dist/storybook/iframe.html +3 -3
- package/dist/storybook/project.json +1 -1
- package/dist/tailwind.config.d.ts +4 -1
- package/dist/tailwind.config.d.ts.map +1 -1
- package/dist/tailwind.config.js +0 -2
- package/dist/tailwind.config.js.map +1 -1
- package/dist/tailwind.config.ts +0 -2
- package/package.json +20 -4
- package/src/legacy/tool-ui/src/AIInline.css +123 -0
- package/src/legacy/tool-ui/src/ActionBar.css +41 -0
- package/src/legacy/tool-ui/src/Admin.css +258 -0
- package/src/legacy/tool-ui/src/AdobeStock.css +3 -0
- package/src/legacy/tool-ui/src/AnalyticsWidget.css +34 -0
- package/src/legacy/tool-ui/src/AnalyticsWidget.ts +158 -0
- package/src/legacy/tool-ui/src/Apis.css +31 -0
- package/src/legacy/tool-ui/src/AppetizeioEmbedded.css +58 -0
- package/src/legacy/tool-ui/src/AssociatedContentWidget.css +68 -0
- package/src/legacy/tool-ui/src/AutoExpand.css +33 -0
- package/src/legacy/tool-ui/src/Avatar.css +31 -0
- package/src/legacy/tool-ui/src/BackgroundTasks.css +29 -0
- package/src/legacy/tool-ui/src/Base.css +80 -0
- package/src/legacy/tool-ui/src/Board.css +160 -0
- package/src/legacy/tool-ui/src/Board.ts +515 -0
- package/src/legacy/tool-ui/src/BroadcastAlertBanner.ts +93 -0
- package/src/legacy/tool-ui/src/BulkUpload.css +51 -0
- package/src/legacy/tool-ui/src/BulkWorkflow.css +47 -0
- package/src/legacy/tool-ui/src/BulkWorkflow.ts +110 -0
- package/src/legacy/tool-ui/src/BulletedList.css +11 -0
- package/src/legacy/tool-ui/src/CIGCluster/README.md +32 -0
- package/src/legacy/tool-ui/src/CIGCluster/index.ts +144 -0
- package/src/legacy/tool-ui/src/CIGCluster.css +60 -0
- package/src/legacy/tool-ui/src/Calendar.css +110 -0
- package/src/legacy/tool-ui/src/Card.css +28 -0
- package/src/legacy/tool-ui/src/Celebrate.ts +87 -0
- package/src/legacy/tool-ui/src/Chart.ts +53 -0
- package/src/legacy/tool-ui/src/ChartCompat.ts +413 -0
- package/src/legacy/tool-ui/src/ChartTable.ts +22 -0
- package/src/legacy/tool-ui/src/Checkbox.css +22 -0
- package/src/legacy/tool-ui/src/CodeMirror.css +173 -0
- package/src/legacy/tool-ui/src/Collections.css +74 -0
- package/src/legacy/tool-ui/src/ColorInputSpectrum.css +140 -0
- package/src/legacy/tool-ui/src/ComboInput.css +253 -0
- package/src/legacy/tool-ui/src/Compat.css +383 -0
- package/src/legacy/tool-ui/src/ComponentStatesMixin.ts +27 -0
- package/src/legacy/tool-ui/src/ContentColors.css +19 -0
- package/src/legacy/tool-ui/src/ContentEdit.css +1065 -0
- package/src/legacy/tool-ui/src/ContentEditDrawer.css +274 -0
- package/src/legacy/tool-ui/src/ContentEditDrawer.ts +217 -0
- package/src/legacy/tool-ui/src/ContentEditPanel.ts +188 -0
- package/src/legacy/tool-ui/src/ContentEditSites.ts +101 -0
- package/src/legacy/tool-ui/src/ContentForm.css +87 -0
- package/src/legacy/tool-ui/src/ContentForm.ts +104 -0
- package/src/legacy/tool-ui/src/ContentInputGroup.css +678 -0
- package/src/legacy/tool-ui/src/ContentInputGroup.ts +86 -0
- package/src/legacy/tool-ui/src/ContentReporting.css +54 -0
- package/src/legacy/tool-ui/src/ContentSelector.css +215 -0
- package/src/legacy/tool-ui/src/ContentSelectorActions.ts +428 -0
- package/src/legacy/tool-ui/src/ContentSummary.css +176 -0
- package/src/legacy/tool-ui/src/ContentSummary.ts +223 -0
- package/src/legacy/tool-ui/src/ContentTemplatesWidget.css +54 -0
- package/src/legacy/tool-ui/src/ContentTools.css +109 -0
- package/src/legacy/tool-ui/src/ContentType.css +27 -0
- package/src/legacy/tool-ui/src/Conversation.css +325 -0
- package/src/legacy/tool-ui/src/Conversation.ts +836 -0
- package/src/legacy/tool-ui/src/CopySiteWidget.css +8 -0
- package/src/legacy/tool-ui/src/CopyToClipboard/README.md +23 -0
- package/src/legacy/tool-ui/src/CopyToClipboard/index.ts +54 -0
- package/src/legacy/tool-ui/src/CopyToClipboard.css +10 -0
- package/src/legacy/tool-ui/src/Crosslinker.css +486 -0
- package/src/legacy/tool-ui/src/Crosslinker.ts +46 -0
- package/src/legacy/tool-ui/src/CustomKeyboard.css +25 -0
- package/src/legacy/tool-ui/src/DashboardRow.css +54 -0
- package/src/legacy/tool-ui/src/DashboardWidget.css +444 -0
- package/src/legacy/tool-ui/src/DateStringField.css +82 -0
- package/src/legacy/tool-ui/src/DateTimeInput.css +11 -0
- package/src/legacy/tool-ui/src/Dialog.css +441 -0
- package/src/legacy/tool-ui/src/Diff.css +280 -0
- package/src/legacy/tool-ui/src/Diff.ts +89 -0
- package/src/legacy/tool-ui/src/Downloads.css +7 -0
- package/src/legacy/tool-ui/src/Dropdown.css +63 -0
- package/src/legacy/tool-ui/src/EditFieldUpdateCache.ts +129 -0
- package/src/legacy/tool-ui/src/EmbeddedInputGroup/README.md +3 -0
- package/src/legacy/tool-ui/src/EmbeddedInputGroup/index.ts +189 -0
- package/src/legacy/tool-ui/src/EmbeddedInputGroup.css +75 -0
- package/src/legacy/tool-ui/src/Enhancement.css +135 -0
- package/src/legacy/tool-ui/src/Entry.ts +893 -0
- package/src/legacy/tool-ui/src/EventEmitterMixin.ts +71 -0
- package/src/legacy/tool-ui/src/ExternalCalendars.ts +34 -0
- package/src/legacy/tool-ui/src/ExternalItemObjects.ts +36 -0
- package/src/legacy/tool-ui/src/FileInput.css +150 -0
- package/src/legacy/tool-ui/src/FocusRegions.ts +530 -0
- package/src/legacy/tool-ui/src/FormFilter.css +93 -0
- package/src/legacy/tool-ui/src/FullscreenView.css +152 -0
- package/src/legacy/tool-ui/src/GCA.css +9 -0
- package/src/legacy/tool-ui/src/Global.d.ts +219 -0
- package/src/legacy/tool-ui/src/GraphQL.css +74 -0
- package/src/legacy/tool-ui/src/GraphQLApis.css +4 -0
- package/src/legacy/tool-ui/src/Guide.css +193 -0
- package/src/legacy/tool-ui/src/GuideField.css +25 -0
- package/src/legacy/tool-ui/src/Hierarchy.css +68 -0
- package/src/legacy/tool-ui/src/Icon/index.css +11 -0
- package/src/legacy/tool-ui/src/Icon/index.ts +69 -0
- package/src/legacy/tool-ui/src/ImageEditor.css +522 -0
- package/src/legacy/tool-ui/src/ImageRecognition.css +11 -0
- package/src/legacy/tool-ui/src/Incompatible.css +42 -0
- package/src/legacy/tool-ui/src/InputRow.css +26 -0
- package/src/legacy/tool-ui/src/Label.css +33 -0
- package/src/legacy/tool-ui/src/LabeledCheckbox.css +13 -0
- package/src/legacy/tool-ui/src/Link.css +4 -0
- package/src/legacy/tool-ui/src/LinkCarousel.css +77 -0
- package/src/legacy/tool-ui/src/LinkList.css +53 -0
- package/src/legacy/tool-ui/src/LinkTable.css +105 -0
- package/src/legacy/tool-ui/src/ListManager.css +27 -0
- package/src/legacy/tool-ui/src/LiveBlog.css +290 -0
- package/src/legacy/tool-ui/src/LiveBlog.ts +47 -0
- package/src/legacy/tool-ui/src/Loader.svg +16 -0
- package/src/legacy/tool-ui/src/LocationMap.css +25 -0
- package/src/legacy/tool-ui/src/LookingGlass.css +31 -0
- package/src/legacy/tool-ui/src/LucideDynamicLoader.ts +31 -0
- package/src/legacy/tool-ui/src/MailPublishing.css +32 -0
- package/src/legacy/tool-ui/src/Mention.css +34 -0
- package/src/legacy/tool-ui/src/MenuView.css +659 -0
- package/src/legacy/tool-ui/src/Message.css +239 -0
- package/src/legacy/tool-ui/src/Month.css +65 -0
- package/src/legacy/tool-ui/src/NavRail/index.css +47 -0
- package/src/legacy/tool-ui/src/NavRail/index.ts +40 -0
- package/src/legacy/tool-ui/src/Notification.css +197 -0
- package/src/legacy/tool-ui/src/Notifications.ts +139 -0
- package/src/legacy/tool-ui/src/Page.css +884 -0
- package/src/legacy/tool-ui/src/PageNav.ts +108 -0
- package/src/legacy/tool-ui/src/PaginatedResult.css +20 -0
- package/src/legacy/tool-ui/src/Pagination.css +119 -0
- package/src/legacy/tool-ui/src/PastePopup.css +11 -0
- package/src/legacy/tool-ui/src/PlaceholderEditableMixin.ts +86 -0
- package/src/legacy/tool-ui/src/Popup.css +413 -0
- package/src/legacy/tool-ui/src/PostPublish.ts +12 -0
- package/src/legacy/tool-ui/src/PrePublish.ts +63 -0
- package/src/legacy/tool-ui/src/Preview.css +134 -0
- package/src/legacy/tool-ui/src/PrivilegeAccessWidget.css +27 -0
- package/src/legacy/tool-ui/src/ProfileDropdown.css +95 -0
- package/src/legacy/tool-ui/src/ProseMirror-table.css +179 -0
- package/src/legacy/tool-ui/src/ProseMirror.css +180 -0
- package/src/legacy/tool-ui/src/ProseMirrorContainer.css +40 -0
- package/src/legacy/tool-ui/src/ProseMirrorEnhancementMenu.css +68 -0
- package/src/legacy/tool-ui/src/ProseMirrorFindReplace.css +132 -0
- package/src/legacy/tool-ui/src/QueryField.css +77 -0
- package/src/legacy/tool-ui/src/README.md +235 -0
- package/src/legacy/tool-ui/src/RadialProgressBar.css +11 -0
- package/src/legacy/tool-ui/src/RadialProgressBar.ts +73 -0
- package/src/legacy/tool-ui/src/Radio.css +22 -0
- package/src/legacy/tool-ui/src/RepeatableContentInputGroup.css +470 -0
- package/src/legacy/tool-ui/src/RepeatableContentSelector.css +177 -0
- package/src/legacy/tool-ui/src/RepeatableTextInput.css +71 -0
- package/src/legacy/tool-ui/src/Revision.css +70 -0
- package/src/legacy/tool-ui/src/Revisions.css +292 -0
- package/src/legacy/tool-ui/src/RichText.css +15 -0
- package/src/legacy/tool-ui/src/SearchControlsToggle.ts +90 -0
- package/src/legacy/tool-ui/src/SearchFields.css +109 -0
- package/src/legacy/tool-ui/src/SearchInput.css +27 -0
- package/src/legacy/tool-ui/src/SearchResult.css +523 -0
- package/src/legacy/tool-ui/src/SearchWidget.css +441 -0
- package/src/legacy/tool-ui/src/SearchWidget.ts +154 -0
- package/src/legacy/tool-ui/src/SearchWidgetAdvanced.css +109 -0
- package/src/legacy/tool-ui/src/SharePreview.css +71 -0
- package/src/legacy/tool-ui/src/SkipLinks.css +23 -0
- package/src/legacy/tool-ui/src/SkipLinks.ts +112 -0
- package/src/legacy/tool-ui/src/SlackAuthentication.css +9 -0
- package/src/legacy/tool-ui/src/Sortable.css +50 -0
- package/src/legacy/tool-ui/src/Spellcheck.css +34 -0
- package/src/legacy/tool-ui/src/StyleEmbeddedContent.css +62 -0
- package/src/legacy/tool-ui/src/Suggestions.css +53 -0
- package/src/legacy/tool-ui/src/Suggestions.ts +209 -0
- package/src/legacy/tool-ui/src/TabBar.css +174 -0
- package/src/legacy/tool-ui/src/TabContainer.css +40 -0
- package/src/legacy/tool-ui/src/Table.css +107 -0
- package/src/legacy/tool-ui/src/TableSizerPopup.css +42 -0
- package/src/legacy/tool-ui/src/Taxonomy.css +60 -0
- package/src/legacy/tool-ui/src/TextInput.css +40 -0
- package/src/legacy/tool-ui/src/Theme.css +64 -0
- package/src/legacy/tool-ui/src/ThemeBundleEditor.css +93 -0
- package/src/legacy/tool-ui/src/TimedContent.css +74 -0
- package/src/legacy/tool-ui/src/Toggleable/README.md +90 -0
- package/src/legacy/tool-ui/src/Toggleable/index.ts +230 -0
- package/src/legacy/tool-ui/src/Translation.css +115 -0
- package/src/legacy/tool-ui/src/Translation.ts +20 -0
- package/src/legacy/tool-ui/src/TreeList.css +45 -0
- package/src/legacy/tool-ui/src/Types.ts +44 -0
- package/src/legacy/tool-ui/src/Utilities.css +7 -0
- package/src/legacy/tool-ui/src/ViewMirror.css +7 -0
- package/src/legacy/tool-ui/src/ViewPreview.css +59 -0
- package/src/legacy/tool-ui/src/ViewWatchers.css +69 -0
- package/src/legacy/tool-ui/src/Viewers.css +21 -0
- package/src/legacy/tool-ui/src/Week.css +23 -0
- package/src/legacy/tool-ui/src/Widget.css +622 -0
- package/src/legacy/tool-ui/src/WorkStreams.css +97 -0
- package/src/legacy/tool-ui/src/Workflow.css +100 -0
- package/src/legacy/tool-ui/src/Workstreams.ts +52 -0
- package/src/legacy/tool-ui/src/WrappingMixin.ts +77 -0
- package/src/legacy/tool-ui/src/additional-tailwind-classes.txt +205 -0
- package/src/legacy/tool-ui/src/bsp-tracking.d.ts +27 -0
- package/src/legacy/tool-ui/src/dialog/README.md +83 -0
- package/src/legacy/tool-ui/src/dialog/index.ts +465 -0
- package/src/legacy/tool-ui/src/dom/README.md +63 -0
- package/src/legacy/tool-ui/src/dom/Tether.ts +135 -0
- package/src/legacy/tool-ui/src/dom/TetherLayout.ts +149 -0
- package/src/legacy/tool-ui/src/dom/aria.ts +123 -0
- package/src/legacy/tool-ui/src/dom/closest.ts +5 -0
- package/src/legacy/tool-ui/src/dom/create.ts +46 -0
- package/src/legacy/tool-ui/src/dom/find.ts +5 -0
- package/src/legacy/tool-ui/src/dom/findAll.ts +5 -0
- package/src/legacy/tool-ui/src/dom/focusable.ts +29 -0
- package/src/legacy/tool-ui/src/dom/ifClick.ts +18 -0
- package/src/legacy/tool-ui/src/dom/ifMatches.ts +17 -0
- package/src/legacy/tool-ui/src/dom/ifUnmodified.ts +16 -0
- package/src/legacy/tool-ui/src/dom/insertBefore.ts +4 -0
- package/src/legacy/tool-ui/src/dom/insertFirst.ts +4 -0
- package/src/legacy/tool-ui/src/dom/insertLast.ts +4 -0
- package/src/legacy/tool-ui/src/dom/keyboard.ts +176 -0
- package/src/legacy/tool-ui/src/dom/onFind.ts +277 -0
- package/src/legacy/tool-ui/src/dom/onFindOnce.ts +31 -0
- package/src/legacy/tool-ui/src/dom/onRTEReady.ts +105 -0
- package/src/legacy/tool-ui/src/dom/onRemove.ts +27 -0
- package/src/legacy/tool-ui/src/dom/onVisible.ts +29 -0
- package/src/legacy/tool-ui/src/dom/popupMenu.d.ts +6 -0
- package/src/legacy/tool-ui/src/dom/popupMenu.js +282 -0
- package/src/legacy/tool-ui/src/dom/previousUntil.ts +22 -0
- package/src/legacy/tool-ui/src/dropdown/README.md +9 -0
- package/src/legacy/tool-ui/src/dropdown/index.ts +105 -0
- package/src/legacy/tool-ui/src/form/CodeInput/CodeMirror.less +168 -0
- package/src/legacy/tool-ui/src/form/CodeInput/CodeMirror.ts +15 -0
- package/src/legacy/tool-ui/src/form/CodeInput/index.ts +87 -0
- package/src/legacy/tool-ui/src/form/FormFilter/README.md +31 -0
- package/src/legacy/tool-ui/src/form/FormFilter/index.ts +340 -0
- package/src/legacy/tool-ui/src/form/Input/index.less +43 -0
- package/src/legacy/tool-ui/src/form/Input/index.ts +92 -0
- package/src/legacy/tool-ui/src/form/SearchInput/README.md +23 -0
- package/src/legacy/tool-ui/src/form/SearchInput/index.ts +89 -0
- package/src/legacy/tool-ui/src/form/TextAreaInput/index.less +11 -0
- package/src/legacy/tool-ui/src/form/TextAreaInput/index.ts +36 -0
- package/src/legacy/tool-ui/src/graphql/GraphQL.ts +49 -0
- package/src/legacy/tool-ui/src/graphql/GraphQLPreview.ts +23 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/011a678e3efe41981754.png +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/1173.080a90c5923854fc48e6.js +2 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/1173.080a90c5923854fc48e6.js.LICENSE.txt +10 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/194137260531ee29a5f2.svg +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/1946.156c6821383df2638492.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/1950.3594aaa4f20df28f39d9.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/1e8408af1a34bdf61457.png +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/2111.3956b5196a514b2c0131.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/2111.a937b0cafa8cf7c039d2.css +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/2129.6b044e5564dd3dbb2f94.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/2314.420883b6638f35f40e70.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/2456.5bcfb782c2ec27de32ed.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/2537.e8f1359bedd328bf57f4.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/2565.4ca31a6a46de85cd3b31.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/270.3011484ff80f3093083a.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/2825.c1342ac2662d11fd4125.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/2947.f96d21c44c05980974ef.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/3532.c358dfbb2cde0f802c7f.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/3543.2d704cf9e170322468e7.js +2 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/3543.2d704cf9e170322468e7.js.LICENSE.txt +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/3611.61837ffa933f74502094.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/3790.74b7037d430ed0a47d0c.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/3995.afbec58662cafb46ec2c.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/4017.c3ed1ca3be18b5c080c8.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/4093.152620c022c3013502b7.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/4114.57fe159e416e716f8cbc.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/4168.a224af7c5c5f6f2ddd7b.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/4374.37147747a254da8f5f8a.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/4450.16dd5684292fd0a7ea7f.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/4478.93f64e6a6ccb35a6c6d4.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/44b38a404dec4643c46b.png +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/4582.243dbaa3d8a8648f6f40.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/4709.4f6b707e225e481a928e.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/4790.10d75f7c7f8e54ce83a1.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/4795.034ebc93a67b7a3d6536.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/4850.6b944738520f69c98070.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/4a4ee777ddc0d4cedee4.png +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/5001.a61ae5608398d5d98ab1.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/5030.1e8b82e1444067d492b1.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/5055.620ccb3717b4a6427f09.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/5098.33613009827cfc80aafb.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/5385.6656501b67424994820b.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/5598.d84773ca53126661bdca.js +2 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/5598.d84773ca53126661bdca.js.LICENSE.txt +11 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/5617.c81ecb679371ca302f1f.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/5718.449113ae2463a3bb8a83.js +2 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/5718.449113ae2463a3bb8a83.js.LICENSE.txt +8 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/6047.291dd2d4b3f79be2c64d.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/6086.7ca02b827506f29db590.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/6216.cf8adc1990ee1111f065.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/6497.b7d8dcbf0e7c8d8d538d.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/6565.0e5abc3bd344c53ae154.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/7033.4cd6efbe12e7aaa35745.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/7202.1e5b8f21215ea6eae20a.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/7361.793163ca71dfac69d9eb.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/75.e45c9aec6e7409bfe21d.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/7524.2990be869cf0a2988d17.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/7554.bb243c1fb06a125f6353.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/7718.a937b0cafa8cf7c039d2.css +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/7718.f5996ddd99f2353c7785.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/7741.0b2789c4e783af835763.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/7769.d47f40cc83035fc2392a.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/7863.2a1d1fd229778a47e10b.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/7923.8a92f55de8387f7a8a35.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/8684.7171c731d38a9873d6df.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/8752.3d02a0f42ec39786fdf0.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/8897.bfeed19f2a4acbb6d67c.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/8934.e90ceca8169fc25e4e1b.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/9277.88b2d89b3ba1c24e7f93.js +2 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/9277.88b2d89b3ba1c24e7f93.js.LICENSE.txt +9 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/9360.bb0352313d610cae11de.js +2 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/9360.bb0352313d610cae11de.js.LICENSE.txt +33 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/9561.64eab18c8c1bbc4ad8ec.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/9576.174d6a99544d17e7416d.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/983.eedba1cd484aa5bee0c4.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/Appetizeio.185641211f7c0e42a455.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/CISpectrum.74db7dc80b3a6797a167.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/Chart.223ea4ee9e3e32b67a26.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/ChartCompat.4dd096dd030942227ec6.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/CodeMirror.5ddffe054374f883d6f8.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/ImageEditor.14e3b3035666436f53a5.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/LocationMap.72a510c0f5ff236f596e.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/RTEProseMirror.e8521581e28e90ef6f30.js +500 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/RTEProseMirror.e8521581e28e90ef6f30.js.LICENSE.txt +8 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/RegionMap.f6f963eda2cb616b9661.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/TimedContent.4bb4dc83ffbe4019dd7b.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/VideoEditor.11c89dda5cf125738a33.js +2 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/VideoEditor.11c89dda5cf125738a33.js.LICENSE.txt +23 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/c6e9c007f41bd4d26c20.png +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/iframeResizer.contentWindow.map +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/iframeResizer.contentWindow.min.js +2 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/iframeResizer.contentWindow.min.js.LICENSE.txt +7 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/lucide.woff2 +0 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/preview.d46d98e5ca9dba0a0f5a.js +1 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/v4.a6f53058dbb04a69aa5c.css +3 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/v4.de442a0b430071a7803d.js +92 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/v4.de442a0b430071a7803d.js.LICENSE.txt +71 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/v5.5ce74760f3fb482bec5e.js +92 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/v5.5ce74760f3fb482bec5e.js.LICENSE.txt +78 -0
- package/src/legacy/tool-ui/src/main/webapp/dist/v5.68dab4082aba302779ed.css +5 -0
- package/src/legacy/tool-ui/src/main/webapp/style/v3/ExternalPreviewFrame.less +6 -0
- package/src/legacy/tool-ui/src/main/webapp/style/v3/WidgetSearchAdvancedQuery.less +24 -0
- package/src/legacy/tool-ui/src/main/webapp/style/v3/element.less +11 -0
- package/src/legacy/tool-ui/src/main/webapp/style/v3/icon/ajax-loader.gif +0 -0
- package/src/legacy/tool-ui/src/main/webapp/style/v3/index.less +98 -0
- package/src/legacy/tool-ui/src/main/webapp/style/v3/repeatable.less +89 -0
- package/src/legacy/tool-ui/src/main/webapp/style/v3/variables.less +53 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ActionBar.less +44 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Admin.less +253 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/AdobeStock.less +20 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Apis.less +43 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/AutoExpand.less +36 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Avatar.less +30 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/BackgroundTasks.less +52 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Badge.less +10 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Board.less +311 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/BulletedList.less +17 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Button.less +105 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/CIGCluster.less +55 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Card.less +53 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Checkbox.less +99 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Collections.less +49 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ColorInputSpectrum.less +319 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ComboInput.less +403 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/CommunityWidget.less +73 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Compat.less +7 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ContentEdit.less +1512 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ContentEditDrawer.less +329 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ContentEditSites.less +5 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ContentForm.less +174 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ContentInputGroup.less +669 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ContentReporting.less +39 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ContentSelector.less +268 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ContentSummary.less +238 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ContentTools.less +73 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ContentType.less +24 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/CopyToClipboard.less +20 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/DashboardRow.less +40 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/DashboardWidget.less +193 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/DataTable.less +52 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/DateStringField.less +148 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/DateTimeInput.less +58 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Diff.less +322 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Downloads.css +11 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Draggable.less +42 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Dropdown.less +76 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/EmbeddedInputGroup.less +141 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Entry.less +157 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ExternalItemImport.less +3 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/FileInput.less +194 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/FormFilter.less +189 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Frame.less +26 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/GraphQLApis.less +4 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Grid.less +48 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Guide.less +209 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/GuideField.less +108 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Hierarchy.less +89 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Highlight.less +5 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Icon.less +53 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/IconButton.less +111 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ImageEditor.less +922 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Input.less +15 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/InputRow.less +35 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Label.less +12 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/LabeledCheckbox.less +35 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Link.less +93 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/LinkCarousel.less +143 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/LinkList.less +150 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/LinkTable.less +112 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/LocationMap.less +62 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/LookingGlass.less +30 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Message.less +189 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Month.less +102 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Notification.less +278 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/NumberBar.less +21 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/NumberedList.less +17 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Page.less +922 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/PaginatedResult.less +89 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Pagination.less +80 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Popup.less +415 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/PrePublish.less +5 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Preview.less +495 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ProfileDropdown.less +66 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/QueryField.less +147 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/RadialProgressBar.less +20 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Repeatable.less +102 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/RepeatableContentInputGroup.less +731 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/RepeatableContentSelector.less +296 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/RepeatableTextInput.less +156 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Reset.less +373 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Revision.less +50 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/RichText.less +32 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/RichTextEditor.less +197 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/SearchFields.less +403 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/SearchInput.less +65 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/SearchResult.less +479 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/SearchWidget.less +697 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/SearchWidgetAdvanced.less +115 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/SharePreview.less +95 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Sortable.less +242 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/StandardForm.less +12 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/StyleEmbeddedContent.less +131 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Suggestions.less +121 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/TabBar.less +258 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/TabContainer.less +26 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Taxonomy.less +84 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Text.less +41 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/TextInput.less +32 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ThemeBundleEditor.less +106 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/TimedContent.less +202 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/TreeList.less +93 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Utils.css +14 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/VideoEditor.less +737 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ViewMirror.less +42 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/ViewPreview.less +147 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Viewers.less +70 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Week.less +32 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Widget.less +224 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/Workflow.less +153 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/appetizeio/AppetizeioEmbedded.less +77 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/bsp-rings-bg.svg +9 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/graphql/GraphQL.less +171 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/mail-publishing/MailPublishing.less +38 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/Mention.less +35 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/ProseMirror.less +759 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/ProseMirrorContainer.less +29 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/ai_inline_manager/views/AIInline.less +244 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/collab_manager/views/AvatarView.less +11 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/collab_manager/views/CollabEditing.less +18 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/custom_keyboard/CustomKeyboard.less +21 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/enhancement_manager/views/Enhancement.less +67 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/enhancement_manager/views/PreviewView.less +45 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/enhancement_manager/views/ProsemirrorEnhancementMenu.less +113 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/find_replace_manager/views/ProseMirrorFindReplace.less +174 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/fullscreen_manager/views/FullscreenView.less +383 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/list_manager/ListManager.less +42 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/menubar/views/MenuView.less +821 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/paste_manager/views/PastePopup.less +5 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/spellcheck/spellcheck.less +73 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/table_manager/views/ProseMirror-table.less +137 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/rte/plugins/table_manager/views/TableSizerPopup.less +19 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/theme/Theme.less +224 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/translation/Translation.less +114 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/AnalyticsWidget.less +18 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/AssignmentContent.less +19 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/AssignmentDeskDashboard.less +410 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/AssignmentDeskRelatedWidget.less +3 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/AssignmentFilters.less +23 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/AssociatedContentWidget.less +92 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/BulkUpload.less +65 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/BulkWorkflow.less +103 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/Calendar.less +109 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/CalendarAccessOverview.less +26 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/CalendarEventSummary.less +15 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/ClosableWindow.less +3 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/ContentColors.less +17 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/ContentTemplatesWidget.less +123 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/Conversation.less +537 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/CopySiteWidget.less +13 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/FormSubmission.less +3 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/LiveBlog.less +158 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/PitchAssignments.less +16 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/PitchContent.less +10 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/Revisions.less +280 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/SavedSearch.less +13 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/SemRushDashboardWidget.less +6 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/SlackAuthentication.less +13 -0
- package/src/legacy/tool-ui/src/main/webapp/v4/widget/WorkStreams.less +39 -0
- package/src/legacy/tool-ui/src/platform.ts +25 -0
- package/src/legacy/tool-ui/src/rtc/Socket.ts +205 -0
- package/src/legacy/tool-ui/src/rtc/index.ts +276 -0
- package/src/legacy/tool-ui/src/table/README.md +3 -0
- package/src/legacy/tool-ui/src/table/index.ts +107 -0
- package/src/legacy/tool-ui/src/theme/ColorRotator.ts +32 -0
- package/src/legacy/tool-ui/src/theme/Theme.ts +516 -0
- package/src/legacy/tool-ui/src/theme/index.ts +7 -0
- package/src/legacy/tool-ui/src/util/README.md +42 -0
- package/src/legacy/tool-ui/src/util/debounce.ts +22 -0
- package/src/legacy/tool-ui/src/util/getComponentKey.ts +25 -0
- package/src/legacy/tool-ui/src/util/noise.ts +51 -0
- package/src/legacy/tool-ui/src/util/repaint.ts +17 -0
- package/src/legacy/tool-ui/src/util/storage.ts +21 -0
- package/src/legacy/tool-ui/src/util/string.ts +15 -0
- package/src/legacy/tool-ui/src/util/svg.ts +46 -0
- package/src/legacy/tool-ui/src/util/throttle.ts +37 -0
- package/src/legacy/tool-ui/src/util/transition.ts +5 -0
- package/src/legacy/tool-ui/src/v5.css +149 -0
- package/src/legacy/tool-ui/src/v5.ts +2081 -0
- package/src/legacy/tool-ui/src/widget/README.md +27 -0
- package/src/legacy/tool-ui/src/widget/index.ts +160 -0
- package/dist/storybook/assets/if-defined-DxmT9CH8.js +0 -1
|
@@ -0,0 +1,2081 @@
|
|
|
1
|
+
import './main/webapp/v4/Entry.js'
|
|
2
|
+
|
|
3
|
+
import './v5.css'
|
|
4
|
+
import {
|
|
5
|
+
getToggle as getPageNavToggle,
|
|
6
|
+
toggle as togglePageNav,
|
|
7
|
+
} from './PageNav'
|
|
8
|
+
|
|
9
|
+
import onFind from './dom/onFind'
|
|
10
|
+
import create from './dom/create'
|
|
11
|
+
import { announce } from './dom/aria'
|
|
12
|
+
import storage from './util/storage'
|
|
13
|
+
import './Celebrate'
|
|
14
|
+
import './ContentEditPanel'
|
|
15
|
+
import './Crosslinker'
|
|
16
|
+
import Dialog from './dialog/index'
|
|
17
|
+
import { getIcon } from './LucideDynamicLoader'
|
|
18
|
+
import './SearchControlsToggle'
|
|
19
|
+
import { onTransitionsEnded } from './util/transition'
|
|
20
|
+
import './Workstreams'
|
|
21
|
+
import onRemove from './dom/onRemove'
|
|
22
|
+
import './NavRail/index'
|
|
23
|
+
import Icon from './Icon/index'
|
|
24
|
+
import throttle from './util/throttle'
|
|
25
|
+
import { v1 as uuid } from 'uuid'
|
|
26
|
+
import {
|
|
27
|
+
focusRegionManager,
|
|
28
|
+
RegionId,
|
|
29
|
+
FOCUS_REGION_SELECTOR,
|
|
30
|
+
} from './FocusRegions'
|
|
31
|
+
import './SkipLinks'
|
|
32
|
+
|
|
33
|
+
/*
|
|
34
|
+
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
|
|
35
|
+
┃ ┌───────────────────────────────────────────────────────┐ ┃
|
|
36
|
+
┃ │ +-+-+-+-+-+-+-+-+-+ │ ┃░░
|
|
37
|
+
┃ │ |A|T|T|E|N|T|I|O|N| │ ┃░░
|
|
38
|
+
┃ │ +-+-+-+-+-+-+-+-+-+ │ ┃░░
|
|
39
|
+
┃ └───────────────────────────────────────────────────────┘ ┃░░
|
|
40
|
+
┃ ┃░░
|
|
41
|
+
┃ The following is bridge code to maintain backwards ┃░░
|
|
42
|
+
┃ compatibility with the v4 UI. The DOM structure, which v4 ┃░░
|
|
43
|
+
┃ relies on, has been preserved and is mostly unchanged. If ┃░░
|
|
44
|
+
┃ anything, the DOM changes are additive. A few elements ┃░░
|
|
45
|
+
┃ and attributes may be new, but those should be rare. ┃░░
|
|
46
|
+
┃ ┃░░
|
|
47
|
+
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛░░
|
|
48
|
+
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
49
|
+
░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
// Remove any custom theme color properties from v4.
|
|
53
|
+
onFind('html', (html: HTMLElement) => {
|
|
54
|
+
for (let i = html.style.length - 1; i >= 0; i--) {
|
|
55
|
+
const nameString = html.style[i]
|
|
56
|
+
if (nameString?.startsWith('--color') || nameString?.startsWith('--rgb')) {
|
|
57
|
+
html.style.removeProperty(nameString)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
// Modify the global page nav.
|
|
63
|
+
onFind('.Page-nav', (nav: HTMLElement) => {
|
|
64
|
+
// Nav is always interactive in v5.
|
|
65
|
+
nav.removeAttribute('inert')
|
|
66
|
+
|
|
67
|
+
// Find the original toggle button and move it to the header.
|
|
68
|
+
// Note that in the future this should be done on the server-side
|
|
69
|
+
const toggle = document.querySelector<HTMLElement>('.Page-navToggle')
|
|
70
|
+
if (!toggle) return
|
|
71
|
+
document
|
|
72
|
+
.querySelector('.Page-header')
|
|
73
|
+
?.insertAdjacentElement('afterbegin', toggle)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
onFind('.Page-navItemToggle', (toggle: HTMLElement) => {
|
|
77
|
+
const icon = new Icon()
|
|
78
|
+
icon.symbol = 'chevron-down'
|
|
79
|
+
toggle?.prepend(icon)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// Reimplement the nav toggle behavior.
|
|
83
|
+
onFind('.Page-navToggle', (toggle: HTMLElement) => {
|
|
84
|
+
function updateToggleLabel(expanded: boolean): void {
|
|
85
|
+
const label = expanded
|
|
86
|
+
? window.BRIGHTSPOT?.ui.tooltips.menu.close
|
|
87
|
+
: window.BRIGHTSPOT?.ui.tooltips.menu.open
|
|
88
|
+
toggle.title = label
|
|
89
|
+
toggle.ariaLabel = label
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
toggle.onclick = () => {
|
|
93
|
+
const isExpanding = toggle.ariaExpanded === 'false'
|
|
94
|
+
toggle.ariaExpanded = isExpanding ? 'true' : 'false'
|
|
95
|
+
updateToggleLabel(isExpanding)
|
|
96
|
+
togglePageNav(isExpanding ? 'ON' : 'OFF')
|
|
97
|
+
|
|
98
|
+
const navLinks = document.querySelectorAll<HTMLElement>(
|
|
99
|
+
'.Page-main > btu-nav-rail a',
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
const page = toggle.closest<HTMLElement>('.Page')
|
|
103
|
+
const rail = document.querySelector<HTMLElement>(
|
|
104
|
+
'.Page-main > btu-nav-rail',
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
page?.classList.add('is-nav-animating')
|
|
108
|
+
if (rail) {
|
|
109
|
+
onTransitionsEnded(rail).then(() =>
|
|
110
|
+
page?.classList.remove('is-nav-animating'),
|
|
111
|
+
)
|
|
112
|
+
} else {
|
|
113
|
+
page?.classList.remove('is-nav-animating')
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// When page nav is toggled close, update the tooltips to the item name
|
|
117
|
+
if (!isExpanding) {
|
|
118
|
+
navLinks.forEach((item: HTMLElement) => {
|
|
119
|
+
item.title = item.querySelector(':scope > span')?.textContent ?? ''
|
|
120
|
+
})
|
|
121
|
+
} else {
|
|
122
|
+
// When page nav is toggled open, update the tooltips on
|
|
123
|
+
// expandable items to expand/collapse
|
|
124
|
+
navLinks.forEach((item: HTMLElement) => {
|
|
125
|
+
if (item.parentElement?.hasAttribute('aria-expanded')) {
|
|
126
|
+
item.title = item.parentElement?.classList.contains('is-expanded')
|
|
127
|
+
? window.BRIGHTSPOT?.ui.tooltips.collapse
|
|
128
|
+
: window.BRIGHTSPOT?.ui.tooltips.expand
|
|
129
|
+
} else {
|
|
130
|
+
item.title = ''
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const lastToggleState = getPageNavToggle()
|
|
137
|
+
if (window.matchMedia('not all and (min-width: 768px)').matches) {
|
|
138
|
+
toggle.setAttribute('aria-expanded', 'false')
|
|
139
|
+
} else if (lastToggleState !== null) {
|
|
140
|
+
toggle.setAttribute(
|
|
141
|
+
'aria-expanded',
|
|
142
|
+
lastToggleState === 'ON' ? 'true' : 'false',
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
updateToggleLabel(toggle.ariaExpanded === 'true')
|
|
147
|
+
|
|
148
|
+
// If page nav is collapsed, update the tooltips to the item name
|
|
149
|
+
if (toggle.getAttribute('aria-expanded') === 'false') {
|
|
150
|
+
document
|
|
151
|
+
.querySelectorAll<HTMLElement>('.Page-main > btu-nav-rail a')
|
|
152
|
+
.forEach((item: HTMLElement) => {
|
|
153
|
+
item.title = item.querySelector(':scope > span')?.textContent ?? ''
|
|
154
|
+
})
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
onFind('.toolUserDisplay', (userDisplay) => {
|
|
159
|
+
// Move site switcher out of `.toolUserDisplay` and into the tool title.
|
|
160
|
+
const sitename = userDisplay.querySelector('.toolUserSite')
|
|
161
|
+
if (sitename) {
|
|
162
|
+
const toolTitle = document.querySelector('.toolTitle')
|
|
163
|
+
toolTitle?.append(sitename)
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
onFind('.ContentEdit-top', (top) => {
|
|
168
|
+
const contentEdit = top.closest<HTMLElement>('.ContentEdit')
|
|
169
|
+
|
|
170
|
+
const wips = top.querySelector('.WorkInProgressSaveStatus')
|
|
171
|
+
if (wips) top.classList.add('has-wips')
|
|
172
|
+
|
|
173
|
+
/*
|
|
174
|
+
Moves (Banner) Messages in content edit
|
|
175
|
+
into the sibling to Content edit left called "top"
|
|
176
|
+
so that the message can span across the grid as designed.
|
|
177
|
+
ie:
|
|
178
|
+
|
|
179
|
+
From:
|
|
180
|
+
-- ContentEdit-left ------------------
|
|
181
|
+
| --- ContentEdit-main ------------
|
|
182
|
+
| | --- Message ---------------
|
|
183
|
+
...
|
|
184
|
+
|
|
185
|
+
To:
|
|
186
|
+
-- ContentEdit-left ------------------
|
|
187
|
+
|_____________________________________
|
|
188
|
+
.
|
|
189
|
+
-- ContentEdit-top -------------------
|
|
190
|
+
| --- Message ---------------------
|
|
191
|
+
...
|
|
192
|
+
*/
|
|
193
|
+
const message = contentEdit?.querySelector(
|
|
194
|
+
':scope .ContentEdit-main > .Message',
|
|
195
|
+
)
|
|
196
|
+
if (message) {
|
|
197
|
+
top.insertAdjacentElement('afterend', message)
|
|
198
|
+
|
|
199
|
+
if (!message.closest('.LiveBlogGrid')) {
|
|
200
|
+
const content = message.closest('.Popup-content, .Page-content')
|
|
201
|
+
content?.classList.add('has-message')
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/*
|
|
206
|
+
Moves the title element into the top element.
|
|
207
|
+
This is so the "ContentEdit-overlay" element will be a child of the toolbar.
|
|
208
|
+
*/
|
|
209
|
+
const title = contentEdit?.querySelector<HTMLElement>('.ContentEdit-title')
|
|
210
|
+
if (!title) return
|
|
211
|
+
|
|
212
|
+
const overlayDropdown = title.querySelector<HTMLElement>(
|
|
213
|
+
'button.ContentEdit-overlay:not(empty)',
|
|
214
|
+
)
|
|
215
|
+
if (!overlayDropdown) return
|
|
216
|
+
top.prepend(overlayDropdown)
|
|
217
|
+
top.classList.add('has-overlay')
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
/*
|
|
221
|
+
Hide the search input in favor of the icon button when on mobile.
|
|
222
|
+
Note that this is primarily for keyboard/screen reader a11y.
|
|
223
|
+
*/
|
|
224
|
+
onFind('.Page-header.mobile', (title) => {
|
|
225
|
+
const header = title.closest('.Page-header')
|
|
226
|
+
const search = header?.querySelector('.Page-search')
|
|
227
|
+
if (!search) return
|
|
228
|
+
|
|
229
|
+
search.querySelector(':scope > button')?.removeAttribute('hidden')
|
|
230
|
+
search.querySelector('btu-search-input')?.setAttribute('hidden', 'true')
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
/*
|
|
234
|
+
On mobile screens this adds the ability to click/toggle the page search icon.
|
|
235
|
+
So the search input can show/hide like the design.
|
|
236
|
+
*/
|
|
237
|
+
onFind('.Page .Page-search', (search: HTMLElement) => {
|
|
238
|
+
search.querySelector(':scope > button')?.addEventListener('click', () => {
|
|
239
|
+
search.setAttribute(
|
|
240
|
+
'aria-expanded',
|
|
241
|
+
search.getAttribute('aria-expanded') === 'true' ? 'false' : 'true',
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
if (search.getAttribute('aria-expanded') === 'true') {
|
|
245
|
+
// Show the search input, and hide the icon button (a11y).
|
|
246
|
+
search.querySelector('btu-search-input')?.removeAttribute('hidden')
|
|
247
|
+
search.querySelector(':scope > button')?.setAttribute('hidden', 'true')
|
|
248
|
+
|
|
249
|
+
search
|
|
250
|
+
.querySelector<HTMLInputElement>('btu-search-input > btu-input > input')
|
|
251
|
+
?.click()
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
/*
|
|
257
|
+
When global search is opening, display a loading spinner in the input field.
|
|
258
|
+
*/
|
|
259
|
+
onFind(
|
|
260
|
+
'.Page .Page-search > btu-search-input > btu-input > input',
|
|
261
|
+
(searchInput: HTMLElement) => {
|
|
262
|
+
searchInput.addEventListener('click', () => {
|
|
263
|
+
if (searchInput.closest('.Page:has( .Popup[name="miscSearch"])')) {
|
|
264
|
+
return
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
searchInput.closest('.Page')?.classList.add('is-searchOpening')
|
|
268
|
+
|
|
269
|
+
onFind(
|
|
270
|
+
'.Page dialog:not(:modal)[open] .Popup.loaded.is-ready.is-visible .SearchWidget.is-transitioned',
|
|
271
|
+
(widget: HTMLElement) => {
|
|
272
|
+
widget.closest('.Page')?.classList.remove('is-searchOpening')
|
|
273
|
+
},
|
|
274
|
+
)
|
|
275
|
+
})
|
|
276
|
+
},
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
/*
|
|
280
|
+
When global search is closed, the search input should also collapse.
|
|
281
|
+
Listen on the dialog element so this fires for all close paths (close button, Escape, backdrop).
|
|
282
|
+
*/
|
|
283
|
+
onFind('btu-dialog:has(.SearchWidget.is-global)', (dialog: HTMLElement) => {
|
|
284
|
+
dialog.addEventListener('btu-dialog-closed', () => {
|
|
285
|
+
document
|
|
286
|
+
.querySelector('.Page .Page-search')
|
|
287
|
+
?.setAttribute('aria-expanded', 'false')
|
|
288
|
+
|
|
289
|
+
// Reset the search input visibility when on mobile.
|
|
290
|
+
const search = document.querySelector('.Page-header.mobile .Page-search')
|
|
291
|
+
search?.querySelector('btu-search-input')?.setAttribute('hidden', 'true')
|
|
292
|
+
search?.querySelector(':scope > button')?.removeAttribute('hidden')
|
|
293
|
+
})
|
|
294
|
+
})
|
|
295
|
+
|
|
296
|
+
/*
|
|
297
|
+
When Page-content contains its own query/search field, the global search input should collapse
|
|
298
|
+
by default.
|
|
299
|
+
*/
|
|
300
|
+
onFind('.Page-content:has(.SearchWidget-query)', (page: HTMLElement) => {
|
|
301
|
+
page.previousElementSibling
|
|
302
|
+
?.querySelector('.Page-search')
|
|
303
|
+
?.setAttribute('aria-expanded', 'false')
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
/*
|
|
307
|
+
Clones a site logo (if exists) into the site switcher logo area.
|
|
308
|
+
This could be done on the backend, but it's easier to do it here for now.
|
|
309
|
+
|
|
310
|
+
From:
|
|
311
|
+
-----------------
|
|
312
|
+
<div class="siteSwitch-logo"></div>
|
|
313
|
+
-----------------
|
|
314
|
+
|
|
315
|
+
To:
|
|
316
|
+
-----------------
|
|
317
|
+
<div class="siteSwitch-logo"> <img ...> </div>
|
|
318
|
+
-----------------
|
|
319
|
+
*/
|
|
320
|
+
onFind('.siteSwitch-logo', (target) => {
|
|
321
|
+
const logo = document.querySelector('.Page-title > a > img')
|
|
322
|
+
if (logo) {
|
|
323
|
+
target.append(logo.cloneNode())
|
|
324
|
+
}
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
/** Creates a new label to show the tab item selected in a menu tab bar
|
|
328
|
+
* From:
|
|
329
|
+
-----------------
|
|
330
|
+
<div class="TabBar is-menuOnly">
|
|
331
|
+
<div class="TabBar-menu">
|
|
332
|
+
<button class="TabBar-menuToggle">...</button>
|
|
333
|
+
<div class="TabBar-menuList">...</div>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
-----------------
|
|
337
|
+
|
|
338
|
+
To:
|
|
339
|
+
-----------------
|
|
340
|
+
<div class="TabBar is-menuOnly">
|
|
341
|
+
<div class="TabBar-menu">
|
|
342
|
+
<div class="TabBar-selected-text">...</div>
|
|
343
|
+
<button class="TabBar-menuToggle">...</button>
|
|
344
|
+
<div class="TabBar-menuList">...</div>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
-----------------
|
|
348
|
+
*/
|
|
349
|
+
onFind('.TabBar.is-menuOnly', (container: HTMLElement) => {
|
|
350
|
+
const menu = container.querySelector<HTMLElement>('.TabBar-menu')
|
|
351
|
+
if (!menu) return
|
|
352
|
+
|
|
353
|
+
const selectedItem = menu.querySelector<HTMLElement>(
|
|
354
|
+
':scope .TabBar-menuList > .TabBar-menuItem.is-selected',
|
|
355
|
+
)
|
|
356
|
+
const tabBarSelected = create('div', {
|
|
357
|
+
className: 'TabBar-selected-text',
|
|
358
|
+
innerText: selectedItem?.dataset.tab,
|
|
359
|
+
})
|
|
360
|
+
menu.insertBefore(tabBarSelected, menu.firstElementChild)
|
|
361
|
+
menu
|
|
362
|
+
.querySelectorAll<HTMLElement>('.TabBar-menuItem')
|
|
363
|
+
.forEach((item: HTMLElement) => {
|
|
364
|
+
item.addEventListener('click', (event) => {
|
|
365
|
+
event?.preventDefault()
|
|
366
|
+
tabBarSelected.innerText = item.dataset.tab ?? ''
|
|
367
|
+
})
|
|
368
|
+
})
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
/** Adds a new data-label attribute to search result tab items
|
|
372
|
+
* for icon styling the tab buttons based on their text content
|
|
373
|
+
*/
|
|
374
|
+
onFind('.SearchResult-views > ul', (tabs: HTMLElement) => {
|
|
375
|
+
tabs
|
|
376
|
+
.querySelectorAll<HTMLElement>(':scope > li > a')
|
|
377
|
+
.forEach((tabItem: HTMLElement) => {
|
|
378
|
+
const label = tabItem.textContent?.toLowerCase() ?? ''
|
|
379
|
+
tabItem.dataset.label = label
|
|
380
|
+
tabItem.ariaLabel = label
|
|
381
|
+
tabItem.title = label
|
|
382
|
+
})
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
/** Adds a disabled edit button above empty embedded guides,
|
|
386
|
+
* matching style and placement of existing edit buttons.
|
|
387
|
+
*/
|
|
388
|
+
onFind('.Guide-field > .Guide-embedded:empty', (guide) => {
|
|
389
|
+
const editButton = create('a', {
|
|
390
|
+
className: 'Guide-edit',
|
|
391
|
+
href: '#',
|
|
392
|
+
disabled: true,
|
|
393
|
+
'aria-disabled': true,
|
|
394
|
+
})
|
|
395
|
+
|
|
396
|
+
guide.append(editButton)
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Adds a styling hook to indicate that the Guide edit field
|
|
401
|
+
* has dropdown actions.
|
|
402
|
+
*/
|
|
403
|
+
onFind(
|
|
404
|
+
'.Popup[name="edit-guide"] .RCIG-item > btu-dropdown:not(:empty)',
|
|
405
|
+
(dropdown: HTMLElement) => {
|
|
406
|
+
dropdown?.closest('.RCIG-item')?.classList.add('has-dropdown-actions')
|
|
407
|
+
},
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
/** Applies the following updates to table structure to fit v5 design:
|
|
411
|
+
* Adds a LinkList-wrapper to LinkList tables in DashboardWidgets for horizontal scrolling
|
|
412
|
+
* For LinkTable & LinkList tables, re-orders LinkTable-main or LinkList-main column so that:
|
|
413
|
+
* 1). They are the first column if no checkbox column is present.
|
|
414
|
+
* 2). They are the second column when checkbox column exists in the table.
|
|
415
|
+
*/
|
|
416
|
+
onFind('table', (table: HTMLElement) => {
|
|
417
|
+
if (
|
|
418
|
+
['LinkList', 'LinkTable', 'term-results'].some((cls) =>
|
|
419
|
+
table.classList.contains(cls),
|
|
420
|
+
)
|
|
421
|
+
) {
|
|
422
|
+
const headerRow = table.querySelector<HTMLElement>(':scope > thead > tr')
|
|
423
|
+
const rows = table.querySelectorAll<HTMLElement>('tbody > tr')
|
|
424
|
+
const main = table.querySelector<HTMLElement>(
|
|
425
|
+
'.LinkTable-main, .LinkList-main, .term-stats-head',
|
|
426
|
+
)
|
|
427
|
+
const row = main?.closest('tr')
|
|
428
|
+
if (!row || !main) return
|
|
429
|
+
const hasCheckbox = table.querySelector<HTMLElement>(
|
|
430
|
+
'th > input[type="checkbox"], td > input[type="checkbox"]',
|
|
431
|
+
)
|
|
432
|
+
if (headerRow) {
|
|
433
|
+
const idx = Array.from(row.children).indexOf(main)
|
|
434
|
+
let headerIdx = idx
|
|
435
|
+
for (let i = 0; i < idx; i++) {
|
|
436
|
+
const hItem = headerRow.children[i]
|
|
437
|
+
if (hItem instanceof HTMLTableCellElement && hItem.colSpan > 1) {
|
|
438
|
+
headerIdx = headerIdx - hItem.colSpan + 1
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const thead = headerRow.children[headerIdx]
|
|
443
|
+
if (hasCheckbox) {
|
|
444
|
+
headerRow.insertBefore(thead, headerRow.children[1])
|
|
445
|
+
} else {
|
|
446
|
+
headerRow.insertBefore(thead, headerRow.firstElementChild)
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
rows.forEach((row) => {
|
|
450
|
+
const main = row.querySelector<HTMLElement>(
|
|
451
|
+
':scope > td.LinkTable-main, :scope > td.LinkList-main',
|
|
452
|
+
)
|
|
453
|
+
if (!main) return
|
|
454
|
+
if (hasCheckbox) {
|
|
455
|
+
row.insertBefore(main, row.children[1])
|
|
456
|
+
} else {
|
|
457
|
+
row.insertBefore(main, row.firstElementChild)
|
|
458
|
+
}
|
|
459
|
+
})
|
|
460
|
+
}
|
|
461
|
+
if (
|
|
462
|
+
table.closest(
|
|
463
|
+
'.DashboardWidget:not(.widget-scheduledEvents), .SearchWidget-result, #term-finder-result',
|
|
464
|
+
)
|
|
465
|
+
) {
|
|
466
|
+
const cellCount = table.querySelectorAll<HTMLElement>(
|
|
467
|
+
'tbody tr:first-child > td',
|
|
468
|
+
).length
|
|
469
|
+
/* do not add scrolling to table if:
|
|
470
|
+
* it is a single column table or
|
|
471
|
+
* if it has two columns and one of those is viewers (e.g.: My Activity Widget)
|
|
472
|
+
*/
|
|
473
|
+
if (
|
|
474
|
+
cellCount === 1 ||
|
|
475
|
+
(cellCount === 2 && table.querySelector('tr td.LinkList-viewers'))
|
|
476
|
+
) {
|
|
477
|
+
return
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const parentNode = table.parentElement
|
|
481
|
+
if (!parentNode) return
|
|
482
|
+
if (parentNode.childElementCount > 1) {
|
|
483
|
+
const tableContainer = create('div', {
|
|
484
|
+
className: 'LinkList-wrapper',
|
|
485
|
+
})
|
|
486
|
+
parentNode.insertBefore(tableContainer, table)
|
|
487
|
+
tableContainer.appendChild(table)
|
|
488
|
+
} else {
|
|
489
|
+
parentNode.classList.add('LinkList-wrapper')
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
/* Move the pagination to bottom of Notification container. */
|
|
495
|
+
onFind('.Notification-pagination', (pagination) => {
|
|
496
|
+
pagination
|
|
497
|
+
.closest('.ToolUserNotifications')
|
|
498
|
+
?.querySelector('.Notification')
|
|
499
|
+
?.insertAdjacentElement('beforeend', pagination)
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
/* Move the PaginatedResult (PR) pagination after table. */
|
|
503
|
+
onFind('.PR-pagination', (pagination) => {
|
|
504
|
+
pagination
|
|
505
|
+
.closest('.PR')
|
|
506
|
+
?.querySelector('.LinkList-wrapper, .LinkTable')
|
|
507
|
+
?.insertAdjacentElement('afterend', pagination)
|
|
508
|
+
})
|
|
509
|
+
|
|
510
|
+
/* Move the Dashboard Widget Pagination. */
|
|
511
|
+
onFind('.DashboardWidget-pagination', (pagination) => {
|
|
512
|
+
// Move to bottom of workstream widget
|
|
513
|
+
if (pagination.closest('.p-workStreams')) {
|
|
514
|
+
pagination
|
|
515
|
+
.closest('.p-workStreams')
|
|
516
|
+
?.querySelector('.block:not(:has(+ .block))')
|
|
517
|
+
?.insertAdjacentElement('afterend', pagination)
|
|
518
|
+
} else {
|
|
519
|
+
/* moves pagination after tables for non-calendar dashboard widgets */
|
|
520
|
+
pagination
|
|
521
|
+
.closest('.DashboardWidget:not(.widget-scheduledEvents)')
|
|
522
|
+
?.querySelector('.LinkList-wrapper, .LinkTable, .LinkList')
|
|
523
|
+
?.insertAdjacentElement('afterend', pagination)
|
|
524
|
+
}
|
|
525
|
+
})
|
|
526
|
+
|
|
527
|
+
/* Upgrade the v4 copy button to a btu-copy-to-clipboard element. */
|
|
528
|
+
onFind(
|
|
529
|
+
'.LookingGlass-actions[data-action="copy"]',
|
|
530
|
+
(legacyElement: HTMLElement) => {
|
|
531
|
+
const copyToClipboard = document.createElement('btu-copy-to-clipboard')
|
|
532
|
+
copyToClipboard.classList.add(...legacyElement.classList)
|
|
533
|
+
const key = legacyElement.querySelector(':scope > a')?.getAttribute('href')
|
|
534
|
+
const title = legacyElement.querySelector(':scope > a')?.textContent ?? ''
|
|
535
|
+
const copySuccessMessage =
|
|
536
|
+
legacyElement.querySelector(':scope > span')?.textContent ?? ''
|
|
537
|
+
if (!key) return
|
|
538
|
+
|
|
539
|
+
copyToClipboard.copydata = key
|
|
540
|
+
copyToClipboard.title = title
|
|
541
|
+
copyToClipboard.label = title
|
|
542
|
+
|
|
543
|
+
legacyElement.parentNode?.insertBefore(copyToClipboard, legacyElement)
|
|
544
|
+
legacyElement.remove()
|
|
545
|
+
|
|
546
|
+
copyToClipboard.addEventListener(
|
|
547
|
+
'btu-copy-to-clipboard-copied',
|
|
548
|
+
(event) => {
|
|
549
|
+
if (event.target instanceof HTMLElement) {
|
|
550
|
+
const button = event.target.querySelector('button')
|
|
551
|
+
if (button) button.dataset.successLabel = copySuccessMessage
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (copySuccessMessage) {
|
|
555
|
+
announce(copySuccessMessage)
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
)
|
|
559
|
+
},
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
/*
|
|
563
|
+
This checks for when the mobile breakpoint is reached
|
|
564
|
+
and adds a classname of 'mobile' to the page header.
|
|
565
|
+
Normally we wouldn't do this, but it's easier to share styles
|
|
566
|
+
when you can stack query selectors like:
|
|
567
|
+
---------
|
|
568
|
+
.Page-header.mobile,
|
|
569
|
+
.Page-header:has(.ContentEdit-title--v5) {
|
|
570
|
+
<shared styles>
|
|
571
|
+
}
|
|
572
|
+
---------
|
|
573
|
+
also, @media and @apply cannot be used in conjunction.
|
|
574
|
+
*/
|
|
575
|
+
onFind('.Page-header', (header) => {
|
|
576
|
+
const tabletQuery = window.matchMedia('not all and (min-width: 768px)')
|
|
577
|
+
function handleBreakpoint(matches: boolean) {
|
|
578
|
+
const search = header.querySelector('.Page-search')
|
|
579
|
+
if (matches) {
|
|
580
|
+
header.classList.add('mobile')
|
|
581
|
+
document.body.classList.remove('has-navrail-expanded')
|
|
582
|
+
// Restore mobile search visibility: show icon button, hide input.
|
|
583
|
+
// Reset aria-expanded so CSS driven by it (which hides the button) stays in sync.
|
|
584
|
+
if (search) {
|
|
585
|
+
search.setAttribute('aria-expanded', 'false')
|
|
586
|
+
search.querySelector(':scope > button')?.removeAttribute('hidden')
|
|
587
|
+
search.querySelector('btu-search-input')?.setAttribute('hidden', 'true')
|
|
588
|
+
}
|
|
589
|
+
} else {
|
|
590
|
+
header.classList.remove('mobile')
|
|
591
|
+
// Restore desktop search visibility: show input, hide icon button.
|
|
592
|
+
document.body.classList.toggle(
|
|
593
|
+
'has-navrail-expanded',
|
|
594
|
+
getPageNavToggle() === 'ON',
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
// Restore search visibility when switching from mobile to desktop.
|
|
598
|
+
if (search) {
|
|
599
|
+
search.querySelector('btu-search-input')?.removeAttribute('hidden')
|
|
600
|
+
search
|
|
601
|
+
.querySelector<HTMLElement>(':scope > button')
|
|
602
|
+
?.setAttribute('hidden', 'true')
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
handleBreakpoint(tabletQuery.matches)
|
|
607
|
+
tabletQuery.addEventListener('change', (ev) => handleBreakpoint(ev.matches))
|
|
608
|
+
})
|
|
609
|
+
|
|
610
|
+
/* Formats day of week labels in date picker to show only first two letters */
|
|
611
|
+
onFind(
|
|
612
|
+
['.DateStringCalendar .Month-dowLabel', '.Week-day > .Week-dow'],
|
|
613
|
+
(dateLabelEl: HTMLElement) => {
|
|
614
|
+
const label = dateLabelEl.innerText.substring(0, 2)
|
|
615
|
+
dateLabelEl.innerText = label
|
|
616
|
+
},
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
/*
|
|
620
|
+
Change the custom property used in v5 for status color so that
|
|
621
|
+
it won't conflict and be overridden by the workflow color in v4.
|
|
622
|
+
|
|
623
|
+
Note the variety of markup used for status labels in the v4 UI.
|
|
624
|
+
*/
|
|
625
|
+
onFind(
|
|
626
|
+
'.Revisions-statusLabel, .Message.is-revision, .ContentSummary-link > .Label, .ComboInput-selected .Label, .ContentSelector-search > .Label, .LinkTable-label > .Label',
|
|
627
|
+
(statusLabel: HTMLElement) => {
|
|
628
|
+
const workflowColorFromDataset = statusLabel.dataset.statusColor
|
|
629
|
+
const style = statusLabel.style
|
|
630
|
+
const workflowColor =
|
|
631
|
+
workflowColorFromDataset ?? style.getPropertyValue('background')
|
|
632
|
+
|
|
633
|
+
if (workflowColor) {
|
|
634
|
+
style.setProperty('--status-color', workflowColor)
|
|
635
|
+
statusLabel.dataset.statusColor = workflowColor
|
|
636
|
+
style.removeProperty('background')
|
|
637
|
+
style.removeProperty('color')
|
|
638
|
+
} else {
|
|
639
|
+
style.removeProperty('--workflow-color')
|
|
640
|
+
}
|
|
641
|
+
},
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
/*
|
|
645
|
+
Gives reply button inner text "Reply" or "Reply (1)" based on the number of replies.
|
|
646
|
+
Also moves the reply button to the end of the post.
|
|
647
|
+
*/
|
|
648
|
+
onFind('.Conversation-post .Conversation-reply', (reply: HTMLElement) => {
|
|
649
|
+
if (reply.innerText) {
|
|
650
|
+
reply.innerText = reply.title + ' (' + reply.innerText + ')'
|
|
651
|
+
} else {
|
|
652
|
+
reply.innerText = reply.title
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const post = reply.closest('.Conversation-post')
|
|
656
|
+
post?.append(reply)
|
|
657
|
+
})
|
|
658
|
+
|
|
659
|
+
/* adds title attributes for ul.LinkTable list items to show the entire text on hover */
|
|
660
|
+
onFind('ul.LinkTable', (list: HTMLElement) => {
|
|
661
|
+
const liItems = list.querySelectorAll<HTMLElement>(':scope > li')
|
|
662
|
+
liItems.forEach((item: HTMLElement) => {
|
|
663
|
+
item = item.querySelector('.ViewWatchers-item-name') || item
|
|
664
|
+
item.title = item.textContent || ''
|
|
665
|
+
})
|
|
666
|
+
})
|
|
667
|
+
|
|
668
|
+
onFind('.DashboardWidget-maximize', (maximize: HTMLElement) => {
|
|
669
|
+
if (maximize.closest('.DashboardRow-cell')?.matches('.is-maximized')) {
|
|
670
|
+
maximize.setAttribute('aria-expanded', 'true')
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
maximize.addEventListener('click', async (event) => {
|
|
674
|
+
const target = event.target
|
|
675
|
+
if (!(target instanceof HTMLElement)) return
|
|
676
|
+
|
|
677
|
+
const toggleMaximize = (widget: HTMLElement) => {
|
|
678
|
+
if (widget instanceof HTMLElement) {
|
|
679
|
+
if (maximize.toggleAttribute('aria-expanded')) {
|
|
680
|
+
widget.classList.add('is-maximized')
|
|
681
|
+
document.body.classList.add('has-dashboardWidgetMaximized')
|
|
682
|
+
} else {
|
|
683
|
+
widget.classList.remove('is-maximized')
|
|
684
|
+
document.body.classList.remove('has-dashboardWidgetMaximized')
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
const dc = target.closest<HTMLElement>('.DashboardRow-cell')
|
|
690
|
+
if (!dc) return
|
|
691
|
+
|
|
692
|
+
// Transition the backdrop separately to avoid unwanted transitions.
|
|
693
|
+
if (!dc.classList.contains('is-maximized')) {
|
|
694
|
+
document.body.classList.add('transition-backdrop')
|
|
695
|
+
dc.classList.add('transition-dashboard-widget')
|
|
696
|
+
const transition = document.startViewTransition(() => toggleMaximize(dc))
|
|
697
|
+
await transition.finished
|
|
698
|
+
// De-reference the unique view transition id when the transition is complete.
|
|
699
|
+
dc.classList.remove('transition-dashboard-widget')
|
|
700
|
+
} else {
|
|
701
|
+
document.body.classList.remove('transition-backdrop')
|
|
702
|
+
toggleMaximize(dc)
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
maximize.focus()
|
|
706
|
+
})
|
|
707
|
+
})
|
|
708
|
+
|
|
709
|
+
/* Adds focus point toggle to enable/disable focus ui on image preview */
|
|
710
|
+
onFind('.ImageEditor-groups', (imageSizeGroups: HTMLElement) => {
|
|
711
|
+
const focusToggle = create('button', {
|
|
712
|
+
className: 'ImageEditor-focusToggle',
|
|
713
|
+
type: 'button',
|
|
714
|
+
})
|
|
715
|
+
let groupsDropdown: HTMLElement | null = null
|
|
716
|
+
imageSizeGroups.prepend(focusToggle)
|
|
717
|
+
focusToggle.addEventListener('click', (event) => {
|
|
718
|
+
const isActive = imageSizeGroups.classList.toggle('is-focus-active')
|
|
719
|
+
if (!groupsDropdown) {
|
|
720
|
+
groupsDropdown = imageSizeGroups.querySelector<HTMLElement>(
|
|
721
|
+
':scope > .ComboInput',
|
|
722
|
+
)
|
|
723
|
+
}
|
|
724
|
+
if (!groupsDropdown) return
|
|
725
|
+
groupsDropdown.tabIndex = isActive ? 0 : -1
|
|
726
|
+
})
|
|
727
|
+
})
|
|
728
|
+
|
|
729
|
+
/* Copies search reset button tooltip to inner text */
|
|
730
|
+
onFind('.SearchWidget-reset', (reset: HTMLElement) => {
|
|
731
|
+
reset.innerText = reset.title?.split(' ')[0]
|
|
732
|
+
})
|
|
733
|
+
|
|
734
|
+
/*
|
|
735
|
+
Pulls out the status label from image search result caption so that it can be styled separately.
|
|
736
|
+
*/
|
|
737
|
+
onFind(
|
|
738
|
+
'.SearchResult-images a[data-label-html] > figcaption > .contentTitle--v5 > .Label',
|
|
739
|
+
(status: HTMLElement) => {
|
|
740
|
+
status.classList.add('contentStatus--v5')
|
|
741
|
+
status.parentElement?.insertBefore(status, status.parentElement.firstChild)
|
|
742
|
+
},
|
|
743
|
+
)
|
|
744
|
+
|
|
745
|
+
/*
|
|
746
|
+
Copies image height style from the SearchResult-images dataset so that the CSS can access it.
|
|
747
|
+
*/
|
|
748
|
+
onFind('.SearchResult-images[data-height]', (images: HTMLElement) => {
|
|
749
|
+
images.style.setProperty('--image-height', images.dataset.height + 'dvh')
|
|
750
|
+
|
|
751
|
+
const observer = new MutationObserver((mutationList) => {
|
|
752
|
+
for (const mutation of mutationList) {
|
|
753
|
+
if (
|
|
754
|
+
mutation.type === 'attributes' &&
|
|
755
|
+
mutation.attributeName === 'data-height' &&
|
|
756
|
+
images.dataset.height
|
|
757
|
+
) {
|
|
758
|
+
images.style.setProperty(
|
|
759
|
+
'--image-height',
|
|
760
|
+
images.dataset.height + 'dvh',
|
|
761
|
+
)
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
})
|
|
765
|
+
|
|
766
|
+
observer.observe(images, {
|
|
767
|
+
attributes: true,
|
|
768
|
+
attributeFilter: ['data-height'],
|
|
769
|
+
})
|
|
770
|
+
})
|
|
771
|
+
|
|
772
|
+
/*
|
|
773
|
+
Adds a toggle button to show/hide the image height input field in the search result.
|
|
774
|
+
*/
|
|
775
|
+
onFind('.SearchResult-imageHeightInput', (input: HTMLInputElement) => {
|
|
776
|
+
const button = create('button', {
|
|
777
|
+
className: 'SearchResult-imageHeightToggle v5-only',
|
|
778
|
+
type: 'button',
|
|
779
|
+
ariaExpanded: 'false',
|
|
780
|
+
title: window.BRIGHTSPOT?.ui.tooltips.imageHeightOpen,
|
|
781
|
+
ariaLabel: window.BRIGHTSPOT?.ui.tooltips.imageHeightOpen,
|
|
782
|
+
})
|
|
783
|
+
|
|
784
|
+
const container = create('div', {
|
|
785
|
+
className: 'SearchResult-imageHeightContainer v5-only',
|
|
786
|
+
})
|
|
787
|
+
|
|
788
|
+
// Wrap the input in the container and insert toggle button before input.
|
|
789
|
+
input.insertAdjacentElement('beforebegin', container)
|
|
790
|
+
container.append(input)
|
|
791
|
+
container.prepend(button)
|
|
792
|
+
|
|
793
|
+
button?.addEventListener('click', () => {
|
|
794
|
+
if (button.ariaExpanded === 'false') {
|
|
795
|
+
button.ariaExpanded = 'true'
|
|
796
|
+
button.title = window.BRIGHTSPOT?.ui.tooltips.imageHeightClose
|
|
797
|
+
button.ariaLabel = window.BRIGHTSPOT?.ui.tooltips.imageHeightClose
|
|
798
|
+
input?.classList.add('is-open')
|
|
799
|
+
} else {
|
|
800
|
+
button.ariaExpanded = 'false'
|
|
801
|
+
button.title = window.BRIGHTSPOT?.ui.tooltips.imageHeightOpen
|
|
802
|
+
button.ariaLabel = window.BRIGHTSPOT?.ui.tooltips.imageHeightOpen
|
|
803
|
+
input?.classList.remove('is-open')
|
|
804
|
+
}
|
|
805
|
+
})
|
|
806
|
+
})
|
|
807
|
+
|
|
808
|
+
/* Move the ask-ai wrapper in a level so it can participate in the grid layout. */
|
|
809
|
+
onFind('.Page-askAi', (askAi) => {
|
|
810
|
+
document
|
|
811
|
+
.querySelector('.Page-container')
|
|
812
|
+
?.insertAdjacentElement('beforebegin', askAi)
|
|
813
|
+
document.body.classList.add('has-ask-ai')
|
|
814
|
+
})
|
|
815
|
+
|
|
816
|
+
/*
|
|
817
|
+
Move the notifications panel into the nav rail. In the future this should be
|
|
818
|
+
done on the server-side
|
|
819
|
+
*/
|
|
820
|
+
onFind('.Page-notifications', (notifications: HTMLElement) => {
|
|
821
|
+
const link = notifications.querySelector('a')
|
|
822
|
+
const icon = new Icon()
|
|
823
|
+
icon.symbol = 'bell'
|
|
824
|
+
icon.fill = 'gradient'
|
|
825
|
+
const label = document.createElement('span')
|
|
826
|
+
label.textContent = window.BRIGHTSPOT?.ui.tooltips.notifications
|
|
827
|
+
link?.appendChild(icon)
|
|
828
|
+
link?.appendChild(label)
|
|
829
|
+
|
|
830
|
+
const navRail = document.querySelector('btu-nav-rail')
|
|
831
|
+
if (!navRail) return
|
|
832
|
+
const avatar = navRail.querySelector('.Page-avatar')
|
|
833
|
+
const avatarLi = avatar?.closest('li')
|
|
834
|
+
if (avatarLi) {
|
|
835
|
+
const notificationsLi = document.createElement('li')
|
|
836
|
+
notificationsLi.appendChild(notifications)
|
|
837
|
+
navRail.querySelector('ul')?.insertBefore(notificationsLi, avatarLi)
|
|
838
|
+
} else {
|
|
839
|
+
navRail.addItem(notifications)
|
|
840
|
+
}
|
|
841
|
+
})
|
|
842
|
+
|
|
843
|
+
/*
|
|
844
|
+
Add wrapper to the frames in Create with AI popup.
|
|
845
|
+
Needed to keep the scroll position at the bottom when the frames are updating on response.
|
|
846
|
+
*/
|
|
847
|
+
onFind('.Widget-title + .AIChat-frame', (frame: HTMLElement) => {
|
|
848
|
+
const wrapper = document.createElement('div')
|
|
849
|
+
wrapper.classList.add('AIChat-wrapper')
|
|
850
|
+
wrapper.classList.add('!px-0')
|
|
851
|
+
frame.parentElement?.insertBefore(wrapper, frame)
|
|
852
|
+
wrapper.appendChild(frame)
|
|
853
|
+
})
|
|
854
|
+
|
|
855
|
+
/*
|
|
856
|
+
In board view there's an additional ComboInput and new parent element for
|
|
857
|
+
holding the "show fields" button. To display these in a way
|
|
858
|
+
that makes sense, we make the board fields parent adopt the default sorts.
|
|
859
|
+
|
|
860
|
+
From:
|
|
861
|
+
-----------------
|
|
862
|
+
[Board Fields]
|
|
863
|
+
[Default Sorts]
|
|
864
|
+
-----------------
|
|
865
|
+
|
|
866
|
+
To:
|
|
867
|
+
-----------------
|
|
868
|
+
[Board Fields]
|
|
869
|
+
|- [Default Sorts]
|
|
870
|
+
-----------------
|
|
871
|
+
*/
|
|
872
|
+
onFind('.SearchResult-board-fields', (bfContainer) => {
|
|
873
|
+
const ctx = bfContainer.closest('.SearchResult')
|
|
874
|
+
if (!ctx) return
|
|
875
|
+
const defaultSorts = ctx.querySelector('.SearchResult-sorts')
|
|
876
|
+
if (!defaultSorts) return
|
|
877
|
+
bfContainer.prepend(defaultSorts)
|
|
878
|
+
})
|
|
879
|
+
|
|
880
|
+
/* Unhides the progressBar inside Content Edit workstreams banner
|
|
881
|
+
From:
|
|
882
|
+
-----------------
|
|
883
|
+
<div class="progressBar" style="width:" hidden>
|
|
884
|
+
-----------------
|
|
885
|
+
|
|
886
|
+
To:
|
|
887
|
+
-----------------
|
|
888
|
+
<div class="progressBar" style="--progressPct:">
|
|
889
|
+
|- ::before
|
|
890
|
+
</div
|
|
891
|
+
-----------------
|
|
892
|
+
*/
|
|
893
|
+
onFind('.ContentEdit-workstreamProgress .progressBar', (bar: HTMLElement) => {
|
|
894
|
+
bar.classList.remove('hidden')
|
|
895
|
+
bar.removeAttribute('hidden')
|
|
896
|
+
const progressPct = bar.style.width
|
|
897
|
+
bar.style.removeProperty('width')
|
|
898
|
+
bar.style.setProperty('--progressPct', progressPct)
|
|
899
|
+
})
|
|
900
|
+
|
|
901
|
+
/* Adds toggle functionality to the filters in The Shelf when it is in the right rail */
|
|
902
|
+
onFind('.ContentEditDrawer-widget-filters', (filters) => {
|
|
903
|
+
const isExpanded = storage.get('BSP.ContentEdit.shelfFilters') === 'EXPANDED'
|
|
904
|
+
const mainDrawer = filters.closest('.ContentEditDrawer')
|
|
905
|
+
const drawerWidgetFrame = filters.closest('.ContentEditDrawer-widget')
|
|
906
|
+
if (!mainDrawer || !drawerWidgetFrame) return
|
|
907
|
+
const label = isExpanded
|
|
908
|
+
? window.BRIGHTSPOT?.ui.tooltips.filtersHide
|
|
909
|
+
: window.BRIGHTSPOT?.ui.tooltips.filtersShow
|
|
910
|
+
const button = create('button', {
|
|
911
|
+
className: 'ContentEditDrawer-filtersToggle v5-only',
|
|
912
|
+
type: 'button',
|
|
913
|
+
title: label,
|
|
914
|
+
ariaLabel: label,
|
|
915
|
+
ariaExpanded: isExpanded ? 'true' : 'false',
|
|
916
|
+
})
|
|
917
|
+
|
|
918
|
+
filters?.classList.toggle('is-visible', isExpanded)
|
|
919
|
+
|
|
920
|
+
// The frame gets reloaded after selecting an option in the filters.
|
|
921
|
+
// Replace the toggle with a new one each time this happens so that
|
|
922
|
+
// there are not multiple and it has the right event listener.
|
|
923
|
+
const closeWidget = mainDrawer?.querySelector('.closeButton')
|
|
924
|
+
const existingToggle = mainDrawer?.querySelector(
|
|
925
|
+
'.ContentEditDrawer-filtersToggle',
|
|
926
|
+
)
|
|
927
|
+
existingToggle?.remove()
|
|
928
|
+
closeWidget?.parentNode?.insertBefore(button, closeWidget)
|
|
929
|
+
if (drawerWidgetFrame.classList.contains('is-loading')) {
|
|
930
|
+
drawerWidgetFrame.classList.remove('is-loading')
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
button.addEventListener('click', () => {
|
|
934
|
+
filters?.classList.toggle('is-visible')
|
|
935
|
+
|
|
936
|
+
const isExpanded = filters?.classList.contains('is-visible')
|
|
937
|
+
const label = isExpanded
|
|
938
|
+
? window.BRIGHTSPOT?.ui.tooltips.filtersHide
|
|
939
|
+
: window.BRIGHTSPOT?.ui.tooltips.filtersShow
|
|
940
|
+
|
|
941
|
+
storage.set(
|
|
942
|
+
'BSP.ContentEdit.shelfFilters',
|
|
943
|
+
isExpanded ? 'EXPANDED' : 'COLLAPSED',
|
|
944
|
+
)
|
|
945
|
+
button.title = label
|
|
946
|
+
button.ariaLabel = label
|
|
947
|
+
button.ariaExpanded = isExpanded ? 'true' : 'false'
|
|
948
|
+
})
|
|
949
|
+
filters.addEventListener('change', () => {
|
|
950
|
+
drawerWidgetFrame.classList.add('is-loading')
|
|
951
|
+
})
|
|
952
|
+
})
|
|
953
|
+
|
|
954
|
+
/** Live Blog Post view
|
|
955
|
+
1. Highlights the selected post in left rail list of live blog posts
|
|
956
|
+
If a new post is being created , New Post button is selected
|
|
957
|
+
|
|
958
|
+
2. Mobile view styling for live blog posts:
|
|
959
|
+
Creates a new toggle button that expands/collapses the live blog post list
|
|
960
|
+
From:
|
|
961
|
+
-- Page-main ------------------
|
|
962
|
+
| --- Page-header ------------
|
|
963
|
+
| | --- ContentEdit-title--v5 ---------------
|
|
964
|
+
| | | ...
|
|
965
|
+
| --- Page-content ------------
|
|
966
|
+
| | --- Grid (live blog posts grid)
|
|
967
|
+
...
|
|
968
|
+
|
|
969
|
+
To:
|
|
970
|
+
-- Page-main ------------------
|
|
971
|
+
| --- Page-header ------------
|
|
972
|
+
| | --- LiveBlogPostList-open
|
|
973
|
+
| | --- ContentEdit-title--v5 ---------------
|
|
974
|
+
| | | ...
|
|
975
|
+
| --- Page-content ------------
|
|
976
|
+
| | --- Grid (live blog posts grid)
|
|
977
|
+
...
|
|
978
|
+
*/
|
|
979
|
+
onFind('.LiveBlogPostListNav.loaded', (listContainer: HTMLElement) => {
|
|
980
|
+
const pageContainer = listContainer.closest<HTMLElement>('.Page-main')
|
|
981
|
+
const gridContent = pageContainer?.querySelector<HTMLElement>('.LiveBlogGrid')
|
|
982
|
+
|
|
983
|
+
if (!gridContent) return
|
|
984
|
+
|
|
985
|
+
const newPostButton = listContainer.querySelector<HTMLElement>(
|
|
986
|
+
'.LiveBlogPostList-controls a:not(.LiveBlogPostListNav-refresh)',
|
|
987
|
+
)
|
|
988
|
+
if (!newPostButton) return
|
|
989
|
+
|
|
990
|
+
const selectNavItem = (item: HTMLElement) => {
|
|
991
|
+
const prevSelected = listContainer.querySelector(
|
|
992
|
+
'a.is-selected, tr.is-selected',
|
|
993
|
+
)
|
|
994
|
+
if (prevSelected) {
|
|
995
|
+
prevSelected.classList.remove('is-selected')
|
|
996
|
+
}
|
|
997
|
+
item.classList.add('is-selected')
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
onFind('.LiveBlogGrid .ContentEdit', (form: HTMLElement) => {
|
|
1001
|
+
const isNewPost = form?.classList.contains('is-new')
|
|
1002
|
+
const formId = form?.dataset.objectId
|
|
1003
|
+
gridContent.classList.add('is-visible')
|
|
1004
|
+
const contentPanel = form.querySelector<HTMLElement>(
|
|
1005
|
+
'.ContentEdit-right.is-visible',
|
|
1006
|
+
)
|
|
1007
|
+
if (contentPanel) {
|
|
1008
|
+
contentPanel.classList.remove('is-visible')
|
|
1009
|
+
}
|
|
1010
|
+
if (isNewPost) {
|
|
1011
|
+
selectNavItem(newPostButton)
|
|
1012
|
+
} else {
|
|
1013
|
+
if (!formId) return
|
|
1014
|
+
const post = listContainer.querySelector<HTMLElement>(
|
|
1015
|
+
`tr[data-post-id="${formId}"]`,
|
|
1016
|
+
)
|
|
1017
|
+
if (post) {
|
|
1018
|
+
selectNavItem(post)
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
})
|
|
1022
|
+
|
|
1023
|
+
/* Back to list view button in mobile */
|
|
1024
|
+
let listOpen = gridContent.querySelector<HTMLButtonElement>(
|
|
1025
|
+
'.LiveBlogPostList-open',
|
|
1026
|
+
)
|
|
1027
|
+
if (!listOpen) {
|
|
1028
|
+
listOpen = create('button', {
|
|
1029
|
+
className: 'LiveBlogPostList-open',
|
|
1030
|
+
ariaLabel: window.BRIGHTSPOT?.ui.tooltips.listOpen,
|
|
1031
|
+
title: window.BRIGHTSPOT?.ui.tooltips.listOpen,
|
|
1032
|
+
type: 'button',
|
|
1033
|
+
onclick: () => {
|
|
1034
|
+
gridContent.classList.remove('is-visible')
|
|
1035
|
+
listContainer.classList.add('is-visible')
|
|
1036
|
+
},
|
|
1037
|
+
})
|
|
1038
|
+
gridContent.insertBefore(listOpen, gridContent.firstElementChild)
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
listContainer.addEventListener('click', (event) => {
|
|
1042
|
+
if (event.target instanceof HTMLAnchorElement) {
|
|
1043
|
+
const item = event.target
|
|
1044
|
+
if (
|
|
1045
|
+
item.target === 'postedit' &&
|
|
1046
|
+
(item.closest('.LiveBlogPostList-controls') ||
|
|
1047
|
+
item.closest('tr[data-post-id]'))
|
|
1048
|
+
) {
|
|
1049
|
+
gridContent.classList.add('is-visible')
|
|
1050
|
+
listContainer.classList.remove('is-visible')
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
})
|
|
1054
|
+
})
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
* Resolves a Lucide icon by name and applies the corresponding CSS custom
|
|
1058
|
+
* properties (--compat-icon for the font glyph, --compat-icon-via-mask for
|
|
1059
|
+
* the SVG mask) to the given element.
|
|
1060
|
+
*/
|
|
1061
|
+
function applyIconProperties(name: string, el: HTMLElement): void {
|
|
1062
|
+
const materialToLucide = window.BRIGHTSPOT?.icons?.materialToLucide ?? {}
|
|
1063
|
+
const mappedName = materialToLucide[name] ?? name
|
|
1064
|
+
const iconData = getIcon(mappedName, el)
|
|
1065
|
+
|
|
1066
|
+
el.style.setProperty('--compat-icon', `'${iconData?.compat ?? ''}'`)
|
|
1067
|
+
|
|
1068
|
+
if (iconData?.encodedSvg) {
|
|
1069
|
+
el.style.setProperty(
|
|
1070
|
+
'--compat-icon-via-mask',
|
|
1071
|
+
`url("data:image/svg+xml,${iconData.encodedSvg}")`,
|
|
1072
|
+
)
|
|
1073
|
+
} else {
|
|
1074
|
+
el.style.removeProperty('--compat-icon-via-mask')
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/*
|
|
1079
|
+
Find all elements with icon data attributes and
|
|
1080
|
+
try to associate and render a corresponding Lucide icon.
|
|
1081
|
+
*/
|
|
1082
|
+
onFind(
|
|
1083
|
+
['[data-icon]', '[data-icon-name]', '[data-icon-button-name]'],
|
|
1084
|
+
(el: HTMLElement) => {
|
|
1085
|
+
const name =
|
|
1086
|
+
el.dataset.icon || el.dataset.iconName || el.dataset.iconButtonName
|
|
1087
|
+
|
|
1088
|
+
if (name) {
|
|
1089
|
+
applyIconProperties(name, el)
|
|
1090
|
+
}
|
|
1091
|
+
},
|
|
1092
|
+
)
|
|
1093
|
+
|
|
1094
|
+
// Listen for 'btu-resolve-icon' events from blacklisted areas (toolbar).
|
|
1095
|
+
document.addEventListener('btu-resolve-icon', ((
|
|
1096
|
+
e: CustomEvent<{ name: string; element: HTMLElement }>,
|
|
1097
|
+
) => {
|
|
1098
|
+
const { name, element } = e.detail
|
|
1099
|
+
if (!name || !element) return
|
|
1100
|
+
|
|
1101
|
+
applyIconProperties(name, element)
|
|
1102
|
+
}) as EventListener)
|
|
1103
|
+
|
|
1104
|
+
/* Renders a v5 content edit title into the page header.
|
|
1105
|
+
A MutationObserver on `.ContentLabel` watches for label text changes and refreshes the headline.
|
|
1106
|
+
*/
|
|
1107
|
+
const renderCETitle = (title: HTMLElement) => {
|
|
1108
|
+
const label = title.querySelector<HTMLElement>('.ContentLabel')
|
|
1109
|
+
if (!label) return
|
|
1110
|
+
|
|
1111
|
+
const editPage = title.closest<HTMLElement>('.ContentEdit')
|
|
1112
|
+
if (!editPage) return
|
|
1113
|
+
|
|
1114
|
+
const header = editPage.querySelector<HTMLElement>('.ContentEdit-top')
|
|
1115
|
+
if (!header) return
|
|
1116
|
+
|
|
1117
|
+
const headlineText = () =>
|
|
1118
|
+
(label.textContent ?? '').replace(/(\r\n|\n|\r)/gm, ' ')
|
|
1119
|
+
|
|
1120
|
+
const render = () => {
|
|
1121
|
+
const contentType =
|
|
1122
|
+
title
|
|
1123
|
+
.querySelector<HTMLElement>('.ContentEdit-typeName')
|
|
1124
|
+
?.textContent?.trim() ||
|
|
1125
|
+
title.querySelector<HTMLSelectElement>('select')?.selectedOptions[0]
|
|
1126
|
+
?.text ||
|
|
1127
|
+
''
|
|
1128
|
+
const status = title.querySelector<HTMLSpanElement>('.Label')
|
|
1129
|
+
|
|
1130
|
+
const newTitle = document.createElement('div')
|
|
1131
|
+
newTitle.classList.add('ContentEdit-title--v5')
|
|
1132
|
+
|
|
1133
|
+
const newContentType = document.createElement('span')
|
|
1134
|
+
newContentType.innerText = contentType
|
|
1135
|
+
newContentType.classList.add('ContentEdit-contentType--v5')
|
|
1136
|
+
|
|
1137
|
+
const title2 = document.createElement('span')
|
|
1138
|
+
title2.innerText = headlineText()
|
|
1139
|
+
title2.classList.add('ContentEdit-contentTitle--v5')
|
|
1140
|
+
|
|
1141
|
+
const newStatus = document.createElement('span')
|
|
1142
|
+
newStatus.classList.add('ContentEdit-status--v5')
|
|
1143
|
+
const legacyStatus = title.querySelector<HTMLSpanElement>('span.Label')
|
|
1144
|
+
newStatus.innerText = legacyStatus?.innerText ?? ''
|
|
1145
|
+
// check if the legacyStatus has a class-specified status (e.g. is-scheduled)
|
|
1146
|
+
if (legacyStatus) {
|
|
1147
|
+
const isStatus = Array.from(legacyStatus.classList).find((cls) =>
|
|
1148
|
+
cls.startsWith('is-'),
|
|
1149
|
+
)
|
|
1150
|
+
if (isStatus) {
|
|
1151
|
+
newStatus.classList.add(isStatus)
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
const customWorkflowColor = status?.style?.getPropertyValue('background')
|
|
1156
|
+
if (customWorkflowColor) {
|
|
1157
|
+
newStatus.dataset.statusColor = customWorkflowColor
|
|
1158
|
+
newStatus.style.setProperty('--status-color', customWorkflowColor)
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
// Try to grab status name from the revision banner, as it is often more descriptive
|
|
1162
|
+
const statusBanner = editPage.querySelector<HTMLElement>(
|
|
1163
|
+
'.Message.is-revision',
|
|
1164
|
+
)
|
|
1165
|
+
const statusText = statusBanner?.querySelector<HTMLSpanElement>(
|
|
1166
|
+
'.ContentEdit-revisionLabel',
|
|
1167
|
+
)
|
|
1168
|
+
if (statusText) {
|
|
1169
|
+
newStatus.innerText = statusText.innerText
|
|
1170
|
+
|
|
1171
|
+
// background: fallback for when the global status-label onFind handler hasn't yet set --status-color from data-status-color.
|
|
1172
|
+
const bannerColor =
|
|
1173
|
+
statusBanner?.dataset.statusColor ||
|
|
1174
|
+
statusBanner?.style.getPropertyValue('--status-color') ||
|
|
1175
|
+
statusBanner?.style.getPropertyValue('background')
|
|
1176
|
+
if (bannerColor) {
|
|
1177
|
+
newStatus.dataset.statusColor = bannerColor
|
|
1178
|
+
newStatus.style.setProperty('--status-color', bannerColor)
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
// If status name is empty, default to either "New" or "Published"
|
|
1183
|
+
if (!newStatus.innerText || newStatus.innerText == '') {
|
|
1184
|
+
if (editPage.classList.contains('is-notNew')) {
|
|
1185
|
+
newStatus.innerText = window.BRIGHTSPOT?.ui.workflow.published
|
|
1186
|
+
}
|
|
1187
|
+
if (editPage.classList.contains('is-new')) {
|
|
1188
|
+
newStatus.innerText = window.BRIGHTSPOT?.ui.tooltips.new
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
newTitle.replaceChildren(title2, newContentType, newStatus)
|
|
1193
|
+
|
|
1194
|
+
const existingTitle = header.querySelector('.ContentEdit-title--v5')
|
|
1195
|
+
if (existingTitle) {
|
|
1196
|
+
header.replaceChild(newTitle, existingTitle)
|
|
1197
|
+
} else {
|
|
1198
|
+
header.prepend(newTitle)
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
const observer = new MutationObserver(() => {
|
|
1203
|
+
// Refresh the headline in-place if already rendered; otherwise do a full render.
|
|
1204
|
+
const existingContentTitle = header.querySelector<HTMLElement>(
|
|
1205
|
+
'.ContentEdit-contentTitle--v5',
|
|
1206
|
+
)
|
|
1207
|
+
if (existingContentTitle) {
|
|
1208
|
+
existingContentTitle.innerText = headlineText()
|
|
1209
|
+
} else {
|
|
1210
|
+
render()
|
|
1211
|
+
}
|
|
1212
|
+
})
|
|
1213
|
+
|
|
1214
|
+
observer.observe(label, {
|
|
1215
|
+
attributes: true,
|
|
1216
|
+
})
|
|
1217
|
+
onRemove(editPage, () => observer.disconnect())
|
|
1218
|
+
|
|
1219
|
+
render()
|
|
1220
|
+
|
|
1221
|
+
// Bridge.js adds the capitalized `Message` class to `.message.is-revision` asynchronously.
|
|
1222
|
+
// onFind fires once when it appears — this covers the case where Bridge.js runs after render().
|
|
1223
|
+
// If Bridge.js has already run, render() applies the color directly via `.Message.is-revision`.
|
|
1224
|
+
onFind(editPage, '.Message.is-revision', (statusBanner: HTMLElement) => {
|
|
1225
|
+
const bannerColor =
|
|
1226
|
+
statusBanner.dataset.statusColor ||
|
|
1227
|
+
statusBanner.style.getPropertyValue('--status-color') ||
|
|
1228
|
+
statusBanner.style.getPropertyValue('background')
|
|
1229
|
+
if (!bannerColor) return
|
|
1230
|
+
|
|
1231
|
+
const badge = header.querySelector<HTMLElement>('.ContentEdit-status--v5')
|
|
1232
|
+
if (!badge) return
|
|
1233
|
+
|
|
1234
|
+
const revisionLabel = statusBanner.querySelector<HTMLElement>(
|
|
1235
|
+
'.ContentEdit-revisionLabel',
|
|
1236
|
+
)
|
|
1237
|
+
if (revisionLabel) badge.innerText = revisionLabel.innerText
|
|
1238
|
+
|
|
1239
|
+
badge.dataset.statusColor = bannerColor
|
|
1240
|
+
badge.style.setProperty('--status-color', bannerColor)
|
|
1241
|
+
})
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
if (document.readyState === 'loading') {
|
|
1245
|
+
window.addEventListener('DOMContentLoaded', () => {
|
|
1246
|
+
const title = document.querySelector<HTMLElement>('.ContentEdit-title')
|
|
1247
|
+
if (title) renderCETitle(title)
|
|
1248
|
+
})
|
|
1249
|
+
} else {
|
|
1250
|
+
const title = document.querySelector<HTMLElement>('.ContentEdit-title')
|
|
1251
|
+
if (title) renderCETitle(title)
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
onFind(
|
|
1255
|
+
['.Popup.loaded .ContentEdit-title', '.LiveBlogGrid .ContentEdit-title'],
|
|
1256
|
+
(title: HTMLElement) => {
|
|
1257
|
+
renderCETitle(title)
|
|
1258
|
+
},
|
|
1259
|
+
)
|
|
1260
|
+
|
|
1261
|
+
/* Elements which trigger a dropdown need to be popovers so they render in a top-layer.
|
|
1262
|
+
This is to prevent stacking/position issues rendering them inside a modal dialog (like embedded CE). */
|
|
1263
|
+
onFind(
|
|
1264
|
+
'[aria-haspopup][aria-controls]:not([aria-controls=""])',
|
|
1265
|
+
(trigger: HTMLElement) => {
|
|
1266
|
+
const popup = document.getElementById(
|
|
1267
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1268
|
+
trigger.getAttribute('aria-controls')!,
|
|
1269
|
+
)
|
|
1270
|
+
if (popup === null) return
|
|
1271
|
+
popup.setAttribute('popover', '')
|
|
1272
|
+
trigger.addEventListener('click', () => {
|
|
1273
|
+
popup.showPopover()
|
|
1274
|
+
})
|
|
1275
|
+
},
|
|
1276
|
+
)
|
|
1277
|
+
|
|
1278
|
+
/*
|
|
1279
|
+
`is-local` popups which we don't want treated as popovers
|
|
1280
|
+
and instead should be opened as modals
|
|
1281
|
+
*/
|
|
1282
|
+
function isPopoverBlacklisted(popup: HTMLElement) {
|
|
1283
|
+
if (!popup.dataset?.popupSourceClass) return false
|
|
1284
|
+
|
|
1285
|
+
const blacklist = ['rte2-toolbar-add-table']
|
|
1286
|
+
return blacklist.includes(popup.dataset.popupSourceClass)
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
const renderDialogHeading = (
|
|
1290
|
+
root: HTMLElement | null,
|
|
1291
|
+
selectors: string,
|
|
1292
|
+
hideOriginal = true,
|
|
1293
|
+
) => {
|
|
1294
|
+
if (root === null) {
|
|
1295
|
+
console.warn(
|
|
1296
|
+
'root element provided to `Dialog._renderTitle` is null. Skipping render.',
|
|
1297
|
+
)
|
|
1298
|
+
return
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
const cloned = root.querySelector(`:scope > :where(${selectors})`)
|
|
1302
|
+
const source = root.querySelector(`:scope > * :where(${selectors})`)
|
|
1303
|
+
|
|
1304
|
+
if (source) {
|
|
1305
|
+
if (!cloned) {
|
|
1306
|
+
const clone = source.cloneNode(true) as HTMLElement
|
|
1307
|
+
// If the markup isn't fresh from the server, remove the hidden class.
|
|
1308
|
+
clone.classList.remove('!hidden')
|
|
1309
|
+
root.closest('.Popup-content')?.prepend(clone)
|
|
1310
|
+
if (hideOriginal) source.classList.add('!hidden')
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
const copyDialogAriaLabel = (content: HTMLElement | null) => {
|
|
1316
|
+
if (!content) return
|
|
1317
|
+
|
|
1318
|
+
const legacyPopup = content.closest('.Popup')
|
|
1319
|
+
const dialog = content.closest('dialog[open]')
|
|
1320
|
+
if (!legacyPopup || !dialog) return
|
|
1321
|
+
|
|
1322
|
+
const legacyAriaLabel = legacyPopup.ariaLabel
|
|
1323
|
+
let ariaLabel = dialog.ariaLabel
|
|
1324
|
+
if (ariaLabel && (!legacyAriaLabel || ariaLabel === legacyAriaLabel)) {
|
|
1325
|
+
legacyPopup.removeAttribute('aria-label')
|
|
1326
|
+
return
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
if (legacyAriaLabel) {
|
|
1330
|
+
ariaLabel = legacyAriaLabel
|
|
1331
|
+
legacyPopup.removeAttribute('aria-label')
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// If not already present in v4, aria-label should be set to the heading copied by #renderDialogHeading
|
|
1335
|
+
if (!ariaLabel) {
|
|
1336
|
+
const widgetTitle = legacyPopup.querySelector(
|
|
1337
|
+
':scope > .Popup-content > .Widget-title',
|
|
1338
|
+
)
|
|
1339
|
+
const widgetText =
|
|
1340
|
+
widgetTitle?.querySelector(':scope > span')?.textContent ||
|
|
1341
|
+
widgetTitle?.textContent ||
|
|
1342
|
+
null
|
|
1343
|
+
ariaLabel = widgetText
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
if (!ariaLabel) return
|
|
1347
|
+
dialog.ariaLabel = ariaLabel
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
/* Legacy popups which do NOT load their contents in a frame. eg: ContentSummary */
|
|
1351
|
+
onFind(
|
|
1352
|
+
'btu-dialog dialog[open] .Popup-content:not(:has(> .frame))',
|
|
1353
|
+
(content: HTMLElement) => {
|
|
1354
|
+
renderDialogHeading(content, 'h1,h2,h3,.Widget-title')
|
|
1355
|
+
copyDialogAriaLabel(content)
|
|
1356
|
+
},
|
|
1357
|
+
)
|
|
1358
|
+
|
|
1359
|
+
/* Legacy popups which load their contents in a frame */
|
|
1360
|
+
onFind(
|
|
1361
|
+
'btu-dialog dialog[open] .Popup.loaded .frame.plugin-popup.loaded',
|
|
1362
|
+
async (frame: HTMLElement) => {
|
|
1363
|
+
/* Disabling eslint because the preceeding QS establishes that this is a descendant of a popup. */
|
|
1364
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1365
|
+
await onTransitionsEnded(frame.closest('dialog[open]')!)
|
|
1366
|
+
|
|
1367
|
+
// after transitions are complete, show the content.
|
|
1368
|
+
frame.classList.add('is-transitioned')
|
|
1369
|
+
|
|
1370
|
+
// autofocus a search input if it exists (these don't reliably have an "autofocus" attribute present).
|
|
1371
|
+
frame
|
|
1372
|
+
.querySelector<HTMLInputElement>(
|
|
1373
|
+
'.SearchInput input:not([hidden]), [class^=Search] > btu-input input:not([hidden])',
|
|
1374
|
+
)
|
|
1375
|
+
?.focus()
|
|
1376
|
+
|
|
1377
|
+
// Clone the title from the frame to the popup.
|
|
1378
|
+
const blacklistedFromCloning = [
|
|
1379
|
+
'.SearchWidget.is-global',
|
|
1380
|
+
'.Widget[name="diff"]',
|
|
1381
|
+
'.Widget[name^="create-new-" i]',
|
|
1382
|
+
'.Widget[name^="objectId-" i]:not(:has(.ContentReporting-status))',
|
|
1383
|
+
'.Widget[name^="_copy" i]',
|
|
1384
|
+
'.Widget[name^="DateStringCalendarFrame" i]',
|
|
1385
|
+
]
|
|
1386
|
+
if (!blacklistedFromCloning.some((t) => frame.matches(t))) {
|
|
1387
|
+
renderDialogHeading(
|
|
1388
|
+
frame.closest('.Popup-content'),
|
|
1389
|
+
'h1,h2,h3,.Guide-title,.Widget-title',
|
|
1390
|
+
)
|
|
1391
|
+
copyDialogAriaLabel(frame.closest('.Popup-content'))
|
|
1392
|
+
}
|
|
1393
|
+
},
|
|
1394
|
+
)
|
|
1395
|
+
|
|
1396
|
+
/* Admin edit modals */
|
|
1397
|
+
onFind(
|
|
1398
|
+
'btu-dialog dialog[open] .Popup.loaded .plugin-popup.loaded.Widget[name^="objectId-" i]:has(.Admin > :where(.Admin-nav, .Admin-main))',
|
|
1399
|
+
(frame: HTMLElement) => {
|
|
1400
|
+
renderDialogHeading(
|
|
1401
|
+
frame.closest('.Popup-content'),
|
|
1402
|
+
'.Admin .Widget-title',
|
|
1403
|
+
false,
|
|
1404
|
+
)
|
|
1405
|
+
copyDialogAriaLabel(frame.closest('.Popup-content'))
|
|
1406
|
+
},
|
|
1407
|
+
)
|
|
1408
|
+
|
|
1409
|
+
/*
|
|
1410
|
+
Popovers and Modal Dialogs:
|
|
1411
|
+
Avoiding the "PrePublish" & "QueryField-frame" popup as they are special cases.
|
|
1412
|
+
The "PrePublishPopup" class is added after page load to trigger the auto-open behavior.
|
|
1413
|
+
*/
|
|
1414
|
+
onFind(
|
|
1415
|
+
'.Popup:not(.PrePublish, [name="queryField"]), .PrePublishPopup',
|
|
1416
|
+
(legacyPopup: HTMLElement) => {
|
|
1417
|
+
// Exit early for those which are non-modal popovers.
|
|
1418
|
+
if (
|
|
1419
|
+
legacyPopup.classList.contains('is-local') &&
|
|
1420
|
+
!isPopoverBlacklisted(legacyPopup)
|
|
1421
|
+
) {
|
|
1422
|
+
legacyPopup.setAttribute('popover', '')
|
|
1423
|
+
legacyPopup.addEventListener('toggle', (event: any) => {
|
|
1424
|
+
if (event.newState === 'closed') {
|
|
1425
|
+
legacyPopup.dispatchEvent(
|
|
1426
|
+
new CustomEvent('legacy-popup-closed', {
|
|
1427
|
+
bubbles: true,
|
|
1428
|
+
composed: true,
|
|
1429
|
+
}),
|
|
1430
|
+
)
|
|
1431
|
+
legacyPopup.remove()
|
|
1432
|
+
}
|
|
1433
|
+
})
|
|
1434
|
+
legacyPopup.showPopover()
|
|
1435
|
+
return
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
// Upgrade jQuery Popups to WebComponent.
|
|
1439
|
+
const dialog = new Dialog()
|
|
1440
|
+
dialog.addEventListener('btu-dialog-ready', () => {
|
|
1441
|
+
onRemove.resume()
|
|
1442
|
+
})
|
|
1443
|
+
|
|
1444
|
+
/* This is to pause any onRemove callbacks specific to popup element while it's being moved */
|
|
1445
|
+
onRemove.pause()
|
|
1446
|
+
|
|
1447
|
+
legacyPopup.parentNode?.insertBefore(dialog, legacyPopup)
|
|
1448
|
+
legacyPopup.removeAttribute('role') // Native dialog element has implicit role="dialog"
|
|
1449
|
+
dialog.append(legacyPopup)
|
|
1450
|
+
},
|
|
1451
|
+
)
|
|
1452
|
+
|
|
1453
|
+
/* -------------- [begin] One-off modals --------------------------- */
|
|
1454
|
+
|
|
1455
|
+
/* Special snowflake popup which is part of the page load and needs to immediately open as a modal. */
|
|
1456
|
+
onFind('.PrePublish:has(.Popup-content)', async (popup: HTMLElement) => {
|
|
1457
|
+
setTimeout(() => popup.classList.add('PrePublishPopup'), 0)
|
|
1458
|
+
})
|
|
1459
|
+
|
|
1460
|
+
/* Content edit workflow popover now opens up as a dialog modal in v5 */
|
|
1461
|
+
onFind('.ContentEdit-workflowToggle', (toggle: HTMLElement) => {
|
|
1462
|
+
const workflowPopup = toggle.parentElement?.querySelector<HTMLElement>(
|
|
1463
|
+
'.ContentEdit-workflow',
|
|
1464
|
+
)
|
|
1465
|
+
if (!workflowPopup) return
|
|
1466
|
+
const dialog = new Dialog()
|
|
1467
|
+
dialog.removeOnClose = false
|
|
1468
|
+
dialog.autoOpen = false
|
|
1469
|
+
|
|
1470
|
+
dialog.addEventListener('btu-dialog-ready', () => {
|
|
1471
|
+
onRemove.resume()
|
|
1472
|
+
})
|
|
1473
|
+
|
|
1474
|
+
/* This is to pause any onRemove callbacks specific to popup element while it's being moved */
|
|
1475
|
+
onRemove.pause()
|
|
1476
|
+
|
|
1477
|
+
workflowPopup.parentNode?.insertBefore(dialog, workflowPopup)
|
|
1478
|
+
dialog.append(workflowPopup)
|
|
1479
|
+
|
|
1480
|
+
toggle.addEventListener('click', (event) => {
|
|
1481
|
+
if (toggle.classList.contains('is-open')) {
|
|
1482
|
+
dialog.show()
|
|
1483
|
+
} else {
|
|
1484
|
+
dialog.close(event)
|
|
1485
|
+
}
|
|
1486
|
+
})
|
|
1487
|
+
})
|
|
1488
|
+
|
|
1489
|
+
/* Query field modal */
|
|
1490
|
+
interface QueryInputHTMLElement extends HTMLElement {
|
|
1491
|
+
frame?: QueryFieldFrameElement
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
interface QueryFieldFrameElement extends HTMLElement {
|
|
1495
|
+
_bspDialogSetup?: boolean
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
onFind('.QueryField-toggle', (toggle: HTMLElement) => {
|
|
1499
|
+
const queryField =
|
|
1500
|
+
toggle.parentElement?.querySelector<QueryInputHTMLElement>('.QueryField')
|
|
1501
|
+
if (!queryField) return
|
|
1502
|
+
const queryFieldFrame = queryField.frame
|
|
1503
|
+
if (!queryFieldFrame) return
|
|
1504
|
+
|
|
1505
|
+
// Multiple onFind registrations (v4/QueryField.js + v5.ts + others) each
|
|
1506
|
+
// have their own data-ofc key, so the same toggle can be processed N times.
|
|
1507
|
+
// Guard on the frame element so only the first registration sets up the Dialog.
|
|
1508
|
+
if (queryFieldFrame._bspDialogSetup) return
|
|
1509
|
+
queryFieldFrame._bspDialogSetup = true
|
|
1510
|
+
|
|
1511
|
+
// Upgrade jQuery Popups to WebComponent.
|
|
1512
|
+
const dialog = new Dialog()
|
|
1513
|
+
dialog.removeOnClose = false
|
|
1514
|
+
dialog.autoOpen = false
|
|
1515
|
+
|
|
1516
|
+
// Defer moving the frame and wiring handlers until the Popup is present.
|
|
1517
|
+
// Uses a brightspot-frame-load listener scoped to this specific frame rather
|
|
1518
|
+
// than a global onFind('.QueryField-frame > .Popup') registration, which
|
|
1519
|
+
// would fire for every QueryField-frame Popup on the page and cause
|
|
1520
|
+
// "insertBefore: new child contains the parent" errors when multiple query
|
|
1521
|
+
// fields exist simultaneously.
|
|
1522
|
+
const onFrameLoaded = () => {
|
|
1523
|
+
const popup = queryFieldFrame.querySelector<HTMLElement>('.Popup')
|
|
1524
|
+
if (!popup) return // early dispatch fired before form response; keep listening
|
|
1525
|
+
queryFieldFrame.removeEventListener('brightspot-frame-load', onFrameLoaded)
|
|
1526
|
+
|
|
1527
|
+
dialog.addEventListener('btu-dialog-ready', () => {
|
|
1528
|
+
onRemove.resume()
|
|
1529
|
+
})
|
|
1530
|
+
|
|
1531
|
+
/* This is to pause any onRemove callbacks specific to popup element while it's being moved */
|
|
1532
|
+
onRemove.pause()
|
|
1533
|
+
|
|
1534
|
+
queryFieldFrame.parentNode?.insertBefore(dialog, queryFieldFrame)
|
|
1535
|
+
dialog.append(queryFieldFrame)
|
|
1536
|
+
|
|
1537
|
+
toggle.addEventListener('click', (event) => {
|
|
1538
|
+
dialog.show()
|
|
1539
|
+
})
|
|
1540
|
+
|
|
1541
|
+
dialog.addEventListener('btu-dialog-closed', (event) => {
|
|
1542
|
+
popup.classList.remove('is-visible')
|
|
1543
|
+
})
|
|
1544
|
+
|
|
1545
|
+
const saveButton =
|
|
1546
|
+
queryFieldFrame.querySelector<HTMLElement>('.QueryField-button')
|
|
1547
|
+
if (!saveButton) return
|
|
1548
|
+
saveButton.addEventListener('click', (event) => {
|
|
1549
|
+
dialog.close(event)
|
|
1550
|
+
})
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
queryFieldFrame.addEventListener('brightspot-frame-load', onFrameLoaded)
|
|
1554
|
+
})
|
|
1555
|
+
|
|
1556
|
+
/* Color picker modals */
|
|
1557
|
+
onFind('dialog btu-input > .sp-replacer', (trigger: HTMLElement) => {
|
|
1558
|
+
const popoverId = trigger.getAttribute('aria-controls')
|
|
1559
|
+
if (!popoverId) return
|
|
1560
|
+
const colorPickerPopup = document.querySelector<HTMLElement>(`#${popoverId}`)
|
|
1561
|
+
if (!colorPickerPopup) return
|
|
1562
|
+
colorPickerPopup.setAttribute('popover', '')
|
|
1563
|
+
trigger.addEventListener('click', () => {
|
|
1564
|
+
colorPickerPopup.showPopover()
|
|
1565
|
+
})
|
|
1566
|
+
})
|
|
1567
|
+
|
|
1568
|
+
/*
|
|
1569
|
+
ProseMirror spellcheck suggestions opens as a popover so that it can
|
|
1570
|
+
appear above fullscreen RTE.
|
|
1571
|
+
*/
|
|
1572
|
+
onFind(
|
|
1573
|
+
'.ProseMirror-spellcheck-suggestions.is-opening',
|
|
1574
|
+
(spellcheck: HTMLElement) => {
|
|
1575
|
+
spellcheck.setAttribute('popover', '')
|
|
1576
|
+
spellcheck.showPopover()
|
|
1577
|
+
},
|
|
1578
|
+
)
|
|
1579
|
+
|
|
1580
|
+
/* -------------- [end] One-off modals --------------------------- */
|
|
1581
|
+
|
|
1582
|
+
/*
|
|
1583
|
+
Adds light-dismiss functionality to dialog elements.
|
|
1584
|
+
At the time of this implementation <dialog>'s `closedBy` attribute is experimental.
|
|
1585
|
+
TODO: in the future this could be simplified to use the native `closedby="any"` attribute.
|
|
1586
|
+
See: https://developer.mozilla.org/en-US/docs/Web/API/HTMLDialogElement/closedBy
|
|
1587
|
+
*/
|
|
1588
|
+
document.addEventListener('click', (e) => {
|
|
1589
|
+
const dialog = e.target
|
|
1590
|
+
if (dialog instanceof HTMLDialogElement && dialog.nodeName === 'DIALOG') {
|
|
1591
|
+
const wrapper = dialog.parentElement
|
|
1592
|
+
if (wrapper && wrapper.tagName === Dialog.tagName) {
|
|
1593
|
+
// We don't want the following dialog classes/variants to close on light-dismiss.
|
|
1594
|
+
const preventLightDismissal = wrapper.querySelector(
|
|
1595
|
+
'.Popup.is-fromTop, .Popup.is-noGap',
|
|
1596
|
+
)
|
|
1597
|
+
if (preventLightDismissal) return
|
|
1598
|
+
const d = wrapper as Dialog
|
|
1599
|
+
d.close(e)
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
})
|
|
1603
|
+
|
|
1604
|
+
// Add support for legacy jQuery popup close events. (this can be removed when jQuery is removed).
|
|
1605
|
+
document.addEventListener('legacy.popup.close', (e: Event) => {
|
|
1606
|
+
const popup = e.target
|
|
1607
|
+
if (popup instanceof HTMLElement) {
|
|
1608
|
+
const d = popup.closest('btu-dialog') as Dialog
|
|
1609
|
+
d?.close(e)
|
|
1610
|
+
}
|
|
1611
|
+
})
|
|
1612
|
+
|
|
1613
|
+
document.addEventListener('page-nav-rail-toggled', (e: Event) => {
|
|
1614
|
+
// Close search dialog if it's open.
|
|
1615
|
+
const btuDialog = document.querySelector<Dialog>(
|
|
1616
|
+
'btu-dialog:has(dialog[open] .Popup[name="miscSearch"])',
|
|
1617
|
+
)
|
|
1618
|
+
btuDialog?.closest('.Page')?.classList.remove('is-searchOpening')
|
|
1619
|
+
btuDialog?.close(e)
|
|
1620
|
+
})
|
|
1621
|
+
|
|
1622
|
+
// In month view on a calendar widget,
|
|
1623
|
+
// clicking anywhere on a day with an event should click the event
|
|
1624
|
+
onFind('.Month-day:has(a.calendarEvents)', (day) => {
|
|
1625
|
+
day.addEventListener('click', () => {
|
|
1626
|
+
day.querySelector<HTMLAnchorElement>('a.calendarEvents')?.click()
|
|
1627
|
+
})
|
|
1628
|
+
})
|
|
1629
|
+
|
|
1630
|
+
// Set aria-expanded="true" only when the Ask AI panel is loaded and visible
|
|
1631
|
+
function setAskAiButtonExpanded(expanded: boolean) {
|
|
1632
|
+
const btn = document.querySelector('.Page-askAiButton')
|
|
1633
|
+
if (btn) btn.setAttribute('aria-expanded', expanded ? 'true' : 'false')
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
// Shared click handler for .AskAi-close
|
|
1637
|
+
function handleAskAiCloseClick(evt: Event) {
|
|
1638
|
+
const target = evt.target
|
|
1639
|
+
if (
|
|
1640
|
+
target instanceof HTMLElement &&
|
|
1641
|
+
target.classList.contains('AskAi-close')
|
|
1642
|
+
) {
|
|
1643
|
+
// Update the container state to not loaded when it's closed.
|
|
1644
|
+
// This is helpful for other components to know that Ask AI panel is not active.
|
|
1645
|
+
const askAiContainer = document.getElementById('askAi-container')
|
|
1646
|
+
askAiContainer?.classList.remove('is-loaded')
|
|
1647
|
+
|
|
1648
|
+
setAskAiButtonExpanded(false)
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
// When Ask AI container is loaded
|
|
1653
|
+
onFind('#askAi-container .frame.loaded', (container) => {
|
|
1654
|
+
container.parentElement?.classList.add('is-loaded')
|
|
1655
|
+
setAskAiButtonExpanded(true)
|
|
1656
|
+
|
|
1657
|
+
const askAiPanel = container.closest('.AskAi')
|
|
1658
|
+
const targetElement = askAiPanel || container
|
|
1659
|
+
targetElement.addEventListener('click', handleAskAiCloseClick)
|
|
1660
|
+
})
|
|
1661
|
+
|
|
1662
|
+
// Fallback: If AskAi panel appears separately
|
|
1663
|
+
onFind('.AskAi', (container) => {
|
|
1664
|
+
container.addEventListener('click', handleAskAiCloseClick)
|
|
1665
|
+
})
|
|
1666
|
+
|
|
1667
|
+
onFind('.Page-header .Page-askAiLink', (magicButton) => {
|
|
1668
|
+
/* V4 style collision without removing this class...*/
|
|
1669
|
+
magicButton.classList.remove('IconButton')
|
|
1670
|
+
onFind('.Page-header + btu-nav-rail', (navRail: HTMLElement) => {
|
|
1671
|
+
const magicItem = document.createElement('li')
|
|
1672
|
+
magicItem.appendChild(magicButton)
|
|
1673
|
+
const guideItem = navRail.querySelector('li:has(> .Page-guideLink)')
|
|
1674
|
+
/* If the guide item exists, insert the magic item before it.
|
|
1675
|
+
Otherwise, insert it after the first item in the nav rail. */
|
|
1676
|
+
if (guideItem) {
|
|
1677
|
+
navRail.querySelector('ul')?.insertBefore(magicItem, guideItem)
|
|
1678
|
+
} else {
|
|
1679
|
+
navRail
|
|
1680
|
+
.querySelector('ul > li')
|
|
1681
|
+
?.insertAdjacentElement('afterend', magicItem)
|
|
1682
|
+
}
|
|
1683
|
+
})
|
|
1684
|
+
})
|
|
1685
|
+
|
|
1686
|
+
// Add scroll listener for content edit mobile to allow header to collapse
|
|
1687
|
+
onFind(
|
|
1688
|
+
'.Page-header.mobile ~ .Page-container:not(:has(.ContentEdit-right.is-visible)) .ContentEdit-left',
|
|
1689
|
+
(contentEdit) => {
|
|
1690
|
+
const mobileHeader = document.querySelector<HTMLElement>(
|
|
1691
|
+
'.Page-header.mobile',
|
|
1692
|
+
)
|
|
1693
|
+
if (!mobileHeader) return
|
|
1694
|
+
|
|
1695
|
+
contentEdit.addEventListener(
|
|
1696
|
+
'scroll',
|
|
1697
|
+
throttle(100, () => {
|
|
1698
|
+
const scrollTop = contentEdit.scrollTop
|
|
1699
|
+
// Only collapse the header if the total scroll capacity exceeds the header height (plus the remove threshold).
|
|
1700
|
+
const maxScroll = contentEdit.scrollHeight - contentEdit.clientHeight
|
|
1701
|
+
if (
|
|
1702
|
+
scrollTop > 10 &&
|
|
1703
|
+
maxScroll > mobileHeader.offsetHeight + 10 &&
|
|
1704
|
+
!mobileHeader.classList.contains('is-scrolled')
|
|
1705
|
+
) {
|
|
1706
|
+
mobileHeader.classList.add('is-scrolled')
|
|
1707
|
+
} else if (
|
|
1708
|
+
scrollTop <= 10 &&
|
|
1709
|
+
mobileHeader.classList.contains('is-scrolled')
|
|
1710
|
+
) {
|
|
1711
|
+
mobileHeader.classList.remove('is-scrolled')
|
|
1712
|
+
}
|
|
1713
|
+
}),
|
|
1714
|
+
)
|
|
1715
|
+
},
|
|
1716
|
+
)
|
|
1717
|
+
|
|
1718
|
+
// On the Reports Page, click on a report and go on the recent reports tab.
|
|
1719
|
+
// The view icon in the content selectors should not open the status popup in fullscreen in this case.
|
|
1720
|
+
// This removes the data for popup mode, where ContentSelector-edit usually has noGap set.
|
|
1721
|
+
onFind(
|
|
1722
|
+
[
|
|
1723
|
+
'.ContentReportingWidget .CIG-row[data-field="getPastReportResults"] .ContentSelector-edit',
|
|
1724
|
+
'.CollectionList-item-edit.ContentSelector-edit',
|
|
1725
|
+
],
|
|
1726
|
+
(viewButton) => {
|
|
1727
|
+
viewButton.removeAttribute('data-popup-mode')
|
|
1728
|
+
},
|
|
1729
|
+
)
|
|
1730
|
+
|
|
1731
|
+
onFind('.Popup[name="siteSwitch"]', (el) => {
|
|
1732
|
+
document.getElementById('SiteSwitch-link')?.classList.add('is-active')
|
|
1733
|
+
})
|
|
1734
|
+
|
|
1735
|
+
onFind('.LiveBlogPostList', (el) => {
|
|
1736
|
+
document.body.classList.add('has-liveBlog-postList')
|
|
1737
|
+
})
|
|
1738
|
+
|
|
1739
|
+
onFind('.ToolUserAvatar', (avatar) => {
|
|
1740
|
+
avatar.parentElement?.classList.add('ToolUserAvatar-container')
|
|
1741
|
+
})
|
|
1742
|
+
|
|
1743
|
+
document.addEventListener('legacy-popup-closed', (event) => {
|
|
1744
|
+
if (event.target instanceof HTMLElement) {
|
|
1745
|
+
if (event.target.getAttribute('name') === 'siteSwitch') {
|
|
1746
|
+
document.getElementById('SiteSwitch-link')?.classList.remove('is-active')
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
})
|
|
1750
|
+
|
|
1751
|
+
/**
|
|
1752
|
+
* Prevents accidental button clicks when workflow is in a dragging state.
|
|
1753
|
+
* Ex. if you start dragging on the "Add transition" button, it shouldn't
|
|
1754
|
+
* also trigger a click.
|
|
1755
|
+
*/
|
|
1756
|
+
onFind('.Workflow-state', (state) => {
|
|
1757
|
+
state.addEventListener(
|
|
1758
|
+
'click',
|
|
1759
|
+
(e) => {
|
|
1760
|
+
if (state.classList.contains('is-dragging')) {
|
|
1761
|
+
e.stopPropagation()
|
|
1762
|
+
}
|
|
1763
|
+
},
|
|
1764
|
+
true,
|
|
1765
|
+
)
|
|
1766
|
+
})
|
|
1767
|
+
|
|
1768
|
+
/**
|
|
1769
|
+
* Auto-discover focus regions from backend-rendered [data-focus-region] elements.
|
|
1770
|
+
*
|
|
1771
|
+
* This allows Java developers to register custom focus regions purely through
|
|
1772
|
+
* HTML attributes, without writing any TypeScript. The element must also have
|
|
1773
|
+
* proper a11y attributes (role="region", aria-label) for skip-link support.
|
|
1774
|
+
*
|
|
1775
|
+
* Intentionally placed before imperative registrations — onFind callbacks fire
|
|
1776
|
+
* in registration order, so auto-discovery runs first and imperative register()
|
|
1777
|
+
* calls below will overwrite it, giving imperative registrations precedence.
|
|
1778
|
+
*/
|
|
1779
|
+
onFind(FOCUS_REGION_SELECTOR, (element: HTMLElement) => {
|
|
1780
|
+
focusRegionManager.registerFromAttribute(element)
|
|
1781
|
+
})
|
|
1782
|
+
|
|
1783
|
+
/**
|
|
1784
|
+
* Make various a11y-related adjustments to the Page Guide region.
|
|
1785
|
+
*/
|
|
1786
|
+
onFind('.Page-guide', (guide: HTMLElement) => {
|
|
1787
|
+
const page = guide.closest<HTMLElement>('.Page')
|
|
1788
|
+
if (!page) return
|
|
1789
|
+
|
|
1790
|
+
// Move the guide before the Page-container for better reading order
|
|
1791
|
+
const pageContainer = page?.querySelector('.Page-container')
|
|
1792
|
+
if (pageContainer) {
|
|
1793
|
+
pageContainer.parentNode?.insertBefore(guide, pageContainer)
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
focusRegionManager.register({
|
|
1797
|
+
id: RegionId.PageGuide,
|
|
1798
|
+
element: guide,
|
|
1799
|
+
isHidden: () => !page.classList.contains('is-guideOpen'),
|
|
1800
|
+
})
|
|
1801
|
+
|
|
1802
|
+
function assignRegionLabel() {
|
|
1803
|
+
const title = guide.querySelector<HTMLElement>('.Guide-title-text')
|
|
1804
|
+
if (title) {
|
|
1805
|
+
title.id = 'Guide-title'
|
|
1806
|
+
guide.setAttribute('aria-labelledby', 'Guide-title')
|
|
1807
|
+
} else {
|
|
1808
|
+
guide.removeAttribute('aria-labelledby')
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
const guideVisibilityObserver = new MutationObserver((mutations) => {
|
|
1813
|
+
mutations.forEach((mutation) => {
|
|
1814
|
+
if (mutation.type === 'childList') {
|
|
1815
|
+
assignRegionLabel()
|
|
1816
|
+
}
|
|
1817
|
+
})
|
|
1818
|
+
})
|
|
1819
|
+
|
|
1820
|
+
assignRegionLabel()
|
|
1821
|
+
guideVisibilityObserver.observe(guide, {
|
|
1822
|
+
childList: true,
|
|
1823
|
+
subtree: true,
|
|
1824
|
+
})
|
|
1825
|
+
})
|
|
1826
|
+
|
|
1827
|
+
/**
|
|
1828
|
+
* Register the Page Header region.
|
|
1829
|
+
*/
|
|
1830
|
+
onFind('.Page-header', (header: HTMLElement) => {
|
|
1831
|
+
// Note that <header> implicitly has role="banner"
|
|
1832
|
+
focusRegionManager.register({
|
|
1833
|
+
id: RegionId.PageHeader,
|
|
1834
|
+
element: header,
|
|
1835
|
+
label: window.BRIGHTSPOT?.ui.tooltips.skipLinks.pageHeader,
|
|
1836
|
+
})
|
|
1837
|
+
})
|
|
1838
|
+
|
|
1839
|
+
/**
|
|
1840
|
+
* Register the Nav Rail region.
|
|
1841
|
+
*/
|
|
1842
|
+
onFind('btu-nav-rail[role="navigation"]', (navRail: HTMLElement) => {
|
|
1843
|
+
focusRegionManager.register({
|
|
1844
|
+
id: RegionId.PageNav,
|
|
1845
|
+
element: navRail,
|
|
1846
|
+
label: window.BRIGHTSPOT?.ui.tooltips.skipLinks.pageNavigation,
|
|
1847
|
+
})
|
|
1848
|
+
})
|
|
1849
|
+
|
|
1850
|
+
/**
|
|
1851
|
+
* Register the main region for non-admin & non-content edit pages.
|
|
1852
|
+
*/
|
|
1853
|
+
onFind(
|
|
1854
|
+
'.Page-content:not(:has(> .content-edit), :has(> .Admin))',
|
|
1855
|
+
(content: HTMLElement) => {
|
|
1856
|
+
focusRegionManager.register({
|
|
1857
|
+
id: RegionId.PageMain,
|
|
1858
|
+
element: content,
|
|
1859
|
+
label: window.BRIGHTSPOT?.ui.tooltips.skipLinks.mainContent,
|
|
1860
|
+
})
|
|
1861
|
+
},
|
|
1862
|
+
)
|
|
1863
|
+
|
|
1864
|
+
/**
|
|
1865
|
+
* Register popup/modal header region so that focus can be directed to the close button.
|
|
1866
|
+
*/
|
|
1867
|
+
onFind(
|
|
1868
|
+
'dialog[open] .Popup-content > .Popup-close',
|
|
1869
|
+
(popupContent: HTMLElement) => {
|
|
1870
|
+
focusRegionManager.register({
|
|
1871
|
+
id: RegionId.ModalClose,
|
|
1872
|
+
element: popupContent,
|
|
1873
|
+
})
|
|
1874
|
+
},
|
|
1875
|
+
)
|
|
1876
|
+
|
|
1877
|
+
/**
|
|
1878
|
+
* Register content edit widget regions (ContentEdit-right).
|
|
1879
|
+
* Also set preferred focus to the text input when Conversations widget is active.
|
|
1880
|
+
* Uses MutationObserver to re-register when visibility changes.
|
|
1881
|
+
*/
|
|
1882
|
+
onFind('.ContentEdit-right[role="region"]', (rightPanel: HTMLElement) => {
|
|
1883
|
+
const registerPanel = () => {
|
|
1884
|
+
const conversationPreferredFocus =
|
|
1885
|
+
rightPanel.dataset.rightTab === 'chat_bubble'
|
|
1886
|
+
|
|
1887
|
+
focusRegionManager.register({
|
|
1888
|
+
id: RegionId.ContentEditRight,
|
|
1889
|
+
element: rightPanel,
|
|
1890
|
+
preferredFocus: () => {
|
|
1891
|
+
if (!conversationPreferredFocus) return null
|
|
1892
|
+
return rightPanel.querySelector<HTMLElement>(
|
|
1893
|
+
'.Conversation-newPost .ProseMirror',
|
|
1894
|
+
)
|
|
1895
|
+
},
|
|
1896
|
+
isHidden: () => !rightPanel.classList.contains('is-visible'),
|
|
1897
|
+
})
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
// Initial registration
|
|
1901
|
+
if (rightPanel.classList.contains('is-visible')) registerPanel()
|
|
1902
|
+
|
|
1903
|
+
// Watch for visibility changes and re-register when .is-visible changes
|
|
1904
|
+
const observer = new MutationObserver((mutations) => {
|
|
1905
|
+
for (const mutation of mutations) {
|
|
1906
|
+
if (rightPanel.classList.contains('is-visible')) {
|
|
1907
|
+
registerPanel()
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
})
|
|
1911
|
+
|
|
1912
|
+
observer.observe(rightPanel, {
|
|
1913
|
+
attributeFilter: ['class'],
|
|
1914
|
+
})
|
|
1915
|
+
})
|
|
1916
|
+
|
|
1917
|
+
/**
|
|
1918
|
+
* Register the Content Edit toolbar region.
|
|
1919
|
+
*/
|
|
1920
|
+
onFind('section > .ContentEdit-toolbar', (toolbar: HTMLElement) => {
|
|
1921
|
+
focusRegionManager.register({
|
|
1922
|
+
id: RegionId.ContentEditToolbar,
|
|
1923
|
+
element: toolbar,
|
|
1924
|
+
})
|
|
1925
|
+
})
|
|
1926
|
+
|
|
1927
|
+
/**
|
|
1928
|
+
* Register the Content Edit main (left) region.
|
|
1929
|
+
*/
|
|
1930
|
+
onFind('.ContentEdit-left[role="region"]', (mainPanel: HTMLElement) => {
|
|
1931
|
+
focusRegionManager.register({
|
|
1932
|
+
id: RegionId.PageMain,
|
|
1933
|
+
element: mainPanel,
|
|
1934
|
+
})
|
|
1935
|
+
})
|
|
1936
|
+
|
|
1937
|
+
/**
|
|
1938
|
+
* Register the Content Edit title (top) region.
|
|
1939
|
+
*/
|
|
1940
|
+
onFind('.ContentEdit-top[role="region"]', (titlePanel: HTMLElement) => {
|
|
1941
|
+
focusRegionManager.register({
|
|
1942
|
+
id: RegionId.ContentEditTop,
|
|
1943
|
+
element: titlePanel,
|
|
1944
|
+
})
|
|
1945
|
+
})
|
|
1946
|
+
|
|
1947
|
+
/**
|
|
1948
|
+
* Register the Content Edit preview region.
|
|
1949
|
+
*/
|
|
1950
|
+
onFind(
|
|
1951
|
+
'.ContentEdit-preview[role="region"]:not([inert])',
|
|
1952
|
+
(previewPanel: HTMLElement) => {
|
|
1953
|
+
focusRegionManager.register({
|
|
1954
|
+
id: RegionId.ContentEditPreview,
|
|
1955
|
+
element: previewPanel,
|
|
1956
|
+
isHidden: () => previewPanel.hasAttribute('inert'),
|
|
1957
|
+
})
|
|
1958
|
+
},
|
|
1959
|
+
)
|
|
1960
|
+
|
|
1961
|
+
/**
|
|
1962
|
+
* Register the Content Edit drawer region.
|
|
1963
|
+
*/
|
|
1964
|
+
onFind('.ContentEditDrawer[role="region"].is-open', (drawer: HTMLElement) => {
|
|
1965
|
+
focusRegionManager.register({
|
|
1966
|
+
id: RegionId.ContentEditDrawer,
|
|
1967
|
+
element: drawer,
|
|
1968
|
+
isHidden: () => !drawer.classList.contains('is-open'),
|
|
1969
|
+
})
|
|
1970
|
+
})
|
|
1971
|
+
|
|
1972
|
+
/**
|
|
1973
|
+
* Duplicate id management for situations when multiple content edit forms are open at once.
|
|
1974
|
+
*/
|
|
1975
|
+
/* Generate unique IDs for ContentEditDrawer titles */
|
|
1976
|
+
onFind('#ContentEditDrawer-title', (title: HTMLElement) => {
|
|
1977
|
+
const drawer = title.closest<HTMLElement>('.ContentEditDrawer[role="region"]')
|
|
1978
|
+
if (!drawer) return
|
|
1979
|
+
|
|
1980
|
+
const uniqueId = `ContentEditDrawer-title-${uuid().replace(/-/g, '')}`
|
|
1981
|
+
title.id = uniqueId
|
|
1982
|
+
drawer.setAttribute('aria-labelledby', uniqueId)
|
|
1983
|
+
})
|
|
1984
|
+
|
|
1985
|
+
/* Generate unique IDs for ContentEdit widget titles */
|
|
1986
|
+
onFind('.ContentEdit-right-title', (title: HTMLElement) => {
|
|
1987
|
+
const panel = title.closest<HTMLElement>('[role="region"][data-right-tab]')
|
|
1988
|
+
if (!panel) return
|
|
1989
|
+
|
|
1990
|
+
const baseId = title.id
|
|
1991
|
+
const uniqueId = `${baseId}-${uuid().replace(/-/g, '')}`
|
|
1992
|
+
title.id = uniqueId
|
|
1993
|
+
panel.setAttribute('aria-labelledby', uniqueId)
|
|
1994
|
+
})
|
|
1995
|
+
|
|
1996
|
+
/**
|
|
1997
|
+
* Register the Admin left & main regions. Also ensures that the main region
|
|
1998
|
+
* has an identifiable label.
|
|
1999
|
+
*/
|
|
2000
|
+
onFind('.Admin-nav[role="navigation"]', (adminNav: HTMLElement) => {
|
|
2001
|
+
// Resolve the admin page name from the aria-labelledby heading (e.g., "Plugins")
|
|
2002
|
+
// and construct a label like "Plugins Navigation"
|
|
2003
|
+
const labelledById = adminNav.getAttribute('aria-labelledby')
|
|
2004
|
+
const pageName = labelledById
|
|
2005
|
+
? document.getElementById(labelledById)?.textContent?.trim()
|
|
2006
|
+
: null
|
|
2007
|
+
const navLabel = pageName
|
|
2008
|
+
? `${pageName} ${window.BRIGHTSPOT?.ui.tooltips.skipLinks.navigation}`
|
|
2009
|
+
: window.BRIGHTSPOT?.ui.tooltips.skipLinks.navigation
|
|
2010
|
+
|
|
2011
|
+
focusRegionManager.register({
|
|
2012
|
+
id: RegionId.LeftNav,
|
|
2013
|
+
element: adminNav,
|
|
2014
|
+
label: navLabel,
|
|
2015
|
+
})
|
|
2016
|
+
|
|
2017
|
+
// Register the Admin main region, if it exists.
|
|
2018
|
+
const adminMain = adminNav.nextElementSibling as HTMLElement
|
|
2019
|
+
if (!adminMain || !adminMain.classList.contains('Admin-main')) return
|
|
2020
|
+
|
|
2021
|
+
focusRegionManager.register({
|
|
2022
|
+
id: RegionId.PageMain,
|
|
2023
|
+
element: adminMain,
|
|
2024
|
+
label: window.BRIGHTSPOT?.ui.tooltips.skipLinks.mainContent,
|
|
2025
|
+
})
|
|
2026
|
+
|
|
2027
|
+
// Assign an id to the main region label if one is not already present.
|
|
2028
|
+
const labelId = adminMain.getAttribute('aria-labelledby')
|
|
2029
|
+
if (!labelId) return
|
|
2030
|
+
if (document.getElementById(labelId)) return
|
|
2031
|
+
adminMain
|
|
2032
|
+
.querySelector<HTMLElement>('.Widget-title')
|
|
2033
|
+
?.setAttribute('id', labelId)
|
|
2034
|
+
})
|
|
2035
|
+
|
|
2036
|
+
/**
|
|
2037
|
+
* Ensures focused elements are scrolled into view with proper offset
|
|
2038
|
+
* to account for sticky ActionBar at the bottom of scrollable containers.
|
|
2039
|
+
* Only scrolls when focus arrives via keyboard navigation, not mouse/touch clicks,
|
|
2040
|
+
* to avoid interfering with dropdown positioning and click actions.
|
|
2041
|
+
*/
|
|
2042
|
+
onFind('.ActionBar', (actionBar) => {
|
|
2043
|
+
const widget = actionBar.closest('.Widget')
|
|
2044
|
+
if (!widget) return
|
|
2045
|
+
const widgetTitle = widget.querySelector<HTMLElement>('.Widget-title')
|
|
2046
|
+
|
|
2047
|
+
let lastPointerType = ''
|
|
2048
|
+
const handlePointerdown = (event: Event) => {
|
|
2049
|
+
lastPointerType = (event as PointerEvent).pointerType
|
|
2050
|
+
// Reset after current task so only the immediately following focusin is affected.
|
|
2051
|
+
setTimeout(() => {
|
|
2052
|
+
lastPointerType = ''
|
|
2053
|
+
}, 0)
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
const handleFocusin = (event: Event) => {
|
|
2057
|
+
// Skip scroll-into-view when focus is triggered by mouse or touch click —
|
|
2058
|
+
// smooth scrolling mid-click disrupts dropdown positioning and button actions.
|
|
2059
|
+
if (lastPointerType) return
|
|
2060
|
+
|
|
2061
|
+
const target = event.target
|
|
2062
|
+
if (!(target instanceof HTMLElement)) return
|
|
2063
|
+
|
|
2064
|
+
requestAnimationFrame(() => {
|
|
2065
|
+
const targetRect = target.getBoundingClientRect()
|
|
2066
|
+
if (
|
|
2067
|
+
targetRect.bottom > actionBar.getBoundingClientRect().top ||
|
|
2068
|
+
targetRect.top < (widgetTitle?.getBoundingClientRect().bottom || 0)
|
|
2069
|
+
) {
|
|
2070
|
+
target.scrollIntoView({ block: 'nearest', behavior: 'smooth' })
|
|
2071
|
+
}
|
|
2072
|
+
})
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2075
|
+
widget.addEventListener('pointerdown', handlePointerdown, { passive: true })
|
|
2076
|
+
widget.addEventListener('focusin', handleFocusin, { passive: true })
|
|
2077
|
+
onRemove(widget, () => {
|
|
2078
|
+
widget.removeEventListener('pointerdown', handlePointerdown)
|
|
2079
|
+
widget.removeEventListener('focusin', handleFocusin)
|
|
2080
|
+
})
|
|
2081
|
+
})
|