- openScreenContextMenu(e, false)}
@@ -159,7 +135,6 @@
diff --git a/packages/bbui/package.json b/packages/bbui/package.json index 2caad20bf6..6809f1ffa5 100644 --- a/packages/bbui/package.json +++ b/packages/bbui/package.json @@ -80,7 +80,7 @@ "dayjs": "^1.10.8", "easymde": "^2.16.1", "svelte-dnd-action": "^0.9.8", - "svelte-portal": "^1.0.0" + "svelte-portal": "^2.2.1" }, "resolutions": { "loader-utils": "1.4.1" diff --git a/packages/bbui/src/Actions/click_outside.js b/packages/bbui/src/Actions/click_outside.ts similarity index 63% rename from packages/bbui/src/Actions/click_outside.js rename to packages/bbui/src/Actions/click_outside.ts index 526659cb7a..248a03039e 100644 --- a/packages/bbui/src/Actions/click_outside.js +++ b/packages/bbui/src/Actions/click_outside.ts @@ -1,3 +1,17 @@ +type ClickOutsideCallback = (event: MouseEvent) => void | undefined + +interface ClickOutsideOpts { + callback?: ClickOutsideCallback + anchor?: HTMLElement +} + +interface Handler { + id: number + element: HTMLElement + anchor: HTMLElement + callback?: ClickOutsideCallback +} + // These class names will never trigger a callback if clicked, no matter what const ignoredClasses = [ ".download-js-link", @@ -14,18 +28,20 @@ const conditionallyIgnoredClasses = [ ".drawer-wrapper", ".spectrum-Popover", ] -let clickHandlers = [] -let candidateTarget +let clickHandlers: Handler[] = [] +let candidateTarget: HTMLElement | undefined // Processes a "click outside" event and invokes callbacks if our source element // is valid -const handleClick = event => { +const handleClick = (e: MouseEvent) => { + const target = e.target as HTMLElement + // Ignore click if this is an ignored class - if (event.target.closest('[data-ignore-click-outside="true"]')) { + if (target.closest('[data-ignore-click-outside="true"]')) { return } for (let className of ignoredClasses) { - if (event.target.closest(className)) { + if (target.closest(className)) { return } } @@ -33,41 +49,41 @@ const handleClick = event => { // Process handlers clickHandlers.forEach(handler => { // Check that the click isn't inside the target - if (handler.element.contains(event.target)) { + if (handler.element.contains(target)) { return } // Ignore clicks for certain classes unless we're nested inside them for (let className of conditionallyIgnoredClasses) { const sourceInside = handler.anchor.closest(className) != null - const clickInside = event.target.closest(className) != null + const clickInside = target.closest(className) != null if (clickInside && !sourceInside) { return } } - handler.callback?.(event) + handler.callback?.(e) }) } // On mouse up we only trigger a "click outside" callback if we targetted the // same element that we did on mouse down. This fixes all sorts of issues where // we get annoying callbacks firing when we drag to select text. -const handleMouseUp = e => { +const handleMouseUp = (e: MouseEvent) => { if (candidateTarget === e.target) { handleClick(e) } - candidateTarget = null + candidateTarget = undefined } // On mouse down we store which element was targetted for comparison later -const handleMouseDown = e => { +const handleMouseDown = (e: MouseEvent) => { // Only handle the primary mouse button here. // We handle context menu (right click) events in another handler. if (e.button !== 0) { return } - candidateTarget = e.target + candidateTarget = e.target as HTMLElement // Clear any previous listeners in case of multiple down events, and register // a single mouse up listener @@ -82,7 +98,12 @@ document.addEventListener("contextmenu", handleClick) /** * Adds or updates a click handler */ -const updateHandler = (id, element, anchor, callback) => { +const updateHandler = ( + id: number, + element: HTMLElement, + anchor: HTMLElement, + callback: ClickOutsideCallback | undefined +) => { let existingHandler = clickHandlers.find(x => x.id === id) if (!existingHandler) { clickHandlers.push({ id, element, anchor, callback }) @@ -94,27 +115,52 @@ const updateHandler = (id, element, anchor, callback) => { /** * Removes a click handler */ -const removeHandler = id => { +const removeHandler = (id: number) => { clickHandlers = clickHandlers.filter(x => x.id !== id) } /** - * Svelte action to apply a click outside handler for a certain element + * Svelte action to apply a click outside handler for a certain element. * opts.anchor is an optional param specifying the real root source of the * component being observed. This is required for things like popovers, where * the element using the clickoutside action is the popover, but the popover is * rendered at the root of the DOM somewhere, whereas the popover anchor is the * element we actually want to consider when determining the source component. */ -export default (element, opts) => { +export default ( + element: HTMLElement, + opts?: ClickOutsideOpts | ClickOutsideCallback +) => { const id = Math.random() - const update = newOpts => { - const callback = - newOpts?.callback || (typeof newOpts === "function" ? newOpts : null) - const anchor = newOpts?.anchor || element + + const isCallback = ( + opts?: ClickOutsideOpts | ClickOutsideCallback + ): opts is ClickOutsideCallback => { + return typeof opts === "function" + } + + const isOpts = ( + opts?: ClickOutsideOpts | ClickOutsideCallback + ): opts is ClickOutsideOpts => { + return opts != null && typeof opts === "object" + } + + const update = (newOpts?: ClickOutsideOpts | ClickOutsideCallback) => { + let callback: ClickOutsideCallback | undefined + let anchor = element + if (isCallback(newOpts)) { + callback = newOpts + } else if (isOpts(newOpts)) { + callback = newOpts.callback + if (newOpts.anchor) { + anchor = newOpts.anchor + } + } updateHandler(id, element, anchor, callback) } + update(opts) + return { update, destroy: () => removeHandler(id), diff --git a/packages/bbui/src/Actions/position_dropdown.js b/packages/bbui/src/Actions/position_dropdown.js index e95c7dd1b6..258ce9879d 100644 --- a/packages/bbui/src/Actions/position_dropdown.js +++ b/packages/bbui/src/Actions/position_dropdown.js @@ -1,10 +1,4 @@ -/** - * Valid alignment options are - * - left - * - right - * - left-outside - * - right-outside - **/ +import { PopoverAlignment } from "../constants" // Strategies are defined as [Popover]To[Anchor]. // They can apply for both horizontal and vertical alignment. @@ -149,20 +143,29 @@ export default function positionDropdown(element, opts) { } // Determine X strategy - if (align === "right") { + if (align === PopoverAlignment.Right) { applyXStrategy(Strategies.EndToEnd) - } else if (align === "right-outside" || align === "right-context-menu") { + } else if ( + align === PopoverAlignment.RightOutside || + align === PopoverAlignment.RightContextMenu + ) { applyXStrategy(Strategies.StartToEnd) - } else if (align === "left-outside" || align === "left-context-menu") { + } else if ( + align === PopoverAlignment.LeftOutside || + align === PopoverAlignment.LeftContextMenu + ) { applyXStrategy(Strategies.EndToStart) - } else if (align === "center") { + } else if (align === PopoverAlignment.Center) { applyXStrategy(Strategies.MidPoint) } else { applyXStrategy(Strategies.StartToStart) } // Determine Y strategy - if (align === "right-outside" || align === "left-outside") { + if ( + align === PopoverAlignment.RightOutside || + align === PopoverAlignment.LeftOutside + ) { applyYStrategy(Strategies.MidPoint) } else if ( align === "right-context-menu" || diff --git a/packages/bbui/src/Icon/Icon.svelte b/packages/bbui/src/Icon/Icon.svelte index 7438fab5fd..2f12935f53 100644 --- a/packages/bbui/src/Icon/Icon.svelte +++ b/packages/bbui/src/Icon/Icon.svelte @@ -1,13 +1,10 @@ - - + + + + + +