From 56a4596f4975a5a404bbffa5f8ee1e9ad3a59b1b Mon Sep 17 00:00:00 2001 From: Paul Vilalta Date: Wed, 23 Feb 2022 15:10:43 +0100 Subject: [PATCH 01/24] implementation of GuidedTour component --- components.js | 1 + rollup.config.js | 1 + .../BIMDataGuidedTour/BIMDataGuidedTour.vue | 193 ++++++++++++++++++ .../BIMDataGuidedTour/_BIMDataGuidedTour.scss | 74 +++++++ .../BIMDataGuidedTour/guided-tour-utils.js | 119 +++++++++++ src/BIMDataComponents/index.js | 1 + src/store.js | 7 + src/web/router/router.js | 6 + .../Components/GuidedTour/GuidedTour.vue | 185 +++++++++++++++++ src/web/views/Components/GuidedTour/steps.js | 15 ++ 10 files changed, 602 insertions(+) create mode 100644 src/BIMDataComponents/BIMDataGuidedTour/BIMDataGuidedTour.vue create mode 100644 src/BIMDataComponents/BIMDataGuidedTour/_BIMDataGuidedTour.scss create mode 100644 src/BIMDataComponents/BIMDataGuidedTour/guided-tour-utils.js create mode 100644 src/web/views/Components/GuidedTour/GuidedTour.vue create mode 100644 src/web/views/Components/GuidedTour/steps.js diff --git a/components.js b/components.js index 6378a70b..99b40649 100644 --- a/components.js +++ b/components.js @@ -24,3 +24,4 @@ export { default as BIMDataTextarea } from "./dist/js/BIMDataComponents/BIMDataT export { default as BIMDataTextbox } from "./dist/js/BIMDataComponents/BIMDataTextbox.js"; export { default as BIMDataToggle } from "./dist/js/BIMDataComponents/BIMDataToggle.js"; export { default as BIMDataTooltip } from "./dist/js/BIMDataComponents/BIMDataTooltip.js"; +export { default as BIMDataGuidedTour } from "./dist/js/BIMDataComponents/BIMDataGuidedTour.js"; diff --git a/rollup.config.js b/rollup.config.js index 8eaa0c35..1ff9fb0a 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -177,6 +177,7 @@ function getSingleComponentConfigurations() { "BIMDataTextbox", "BIMDataToggle", "BIMDataTooltip", + "BIMDataGuidedTour", ]; return [ diff --git a/src/BIMDataComponents/BIMDataGuidedTour/BIMDataGuidedTour.vue b/src/BIMDataComponents/BIMDataGuidedTour/BIMDataGuidedTour.vue new file mode 100644 index 00000000..f4e29899 --- /dev/null +++ b/src/BIMDataComponents/BIMDataGuidedTour/BIMDataGuidedTour.vue @@ -0,0 +1,193 @@ + + + + + diff --git a/src/BIMDataComponents/BIMDataGuidedTour/_BIMDataGuidedTour.scss b/src/BIMDataComponents/BIMDataGuidedTour/_BIMDataGuidedTour.scss new file mode 100644 index 00000000..dcff6d15 --- /dev/null +++ b/src/BIMDataComponents/BIMDataGuidedTour/_BIMDataGuidedTour.scss @@ -0,0 +1,74 @@ +.guided-tour-portal { + position: absolute; + z-index: 10000; + inset: 0; + width: 100%; + height: 100%; + + .spotlight { + position: absolute; + border-radius: 4px; + box-shadow: + 0 0 4px 10px rgba(0,0,0,0.5) inset, + 0 0 0 10000vmax rgba(0,0,0,0.5); + } + + .tooltip { + position: absolute; + width: 280px; + padding: 6px 12px; + border-radius: 2px; + background-color: white; + box-shadow: 0 2px 10px 0 rgba(0,0,0,0.3); + display: flex; + flex-direction: column; + justify-content: center; + + transition: opacity 0.1s ease-in-out; + + &__header { + display: flex; + justify-content: flex-end; + align-items: center; + } + + &__content { + min-height: 100px; + max-height: 300px; + overflow-y: auto; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + img { + border-radius: 50%; + height: 180px; + width: 180px; + } + + &__text { + margin: 10px auto; + text-align: center; + color: var(--color-granite); + font-size: calc(var(--spacing-unit) * 1.2); + } + } + + &__footer { + display: flex; + justify-content: space-between; + align-items: center; + + &__step-counter { + span:first-of-type { + color: var(--color-secondary); + font-size: calc(var(--spacing-unit) * 1.6); + } + span:last-of-type { + color: var(--color-granite); + } + } + } + } +} diff --git a/src/BIMDataComponents/BIMDataGuidedTour/guided-tour-utils.js b/src/BIMDataComponents/BIMDataGuidedTour/guided-tour-utils.js new file mode 100644 index 00000000..9d4272fa --- /dev/null +++ b/src/BIMDataComponents/BIMDataGuidedTour/guided-tour-utils.js @@ -0,0 +1,119 @@ +/* eslint-disable */ + +/** + * Parameters + */ + +// Spotlight padding (in px) +const spotPadding = 16; +// Tooltip gap (in px) +const tooltipGap = 4; +// Minimum border gap (in px) +const borderGap = 8; + +/** + * Internal utility functions + */ + +function getElementCoord(elem) { + const { x, y, width: w, height: h } = elem.getBoundingClientRect(); + return { x, y, w, h }; +} + +function getWindowSize() { + return { + W: window.innerWidth, + H: window.innerHeight + }; +} + +function getWindowScroll() { + return { + sX: window.pageXOffset, + sY: window.pageYOffset + }; +} + +function scrollTo({ top, left }, delay = 100) { + return new Promise(resolve => { + let timeout = null; + const scrollHandler = event => { + if (timeout !== null) { + clearTimeout(timeout); + } + timeout = setTimeout(() => { + window.removeEventListener("scroll", scrollHandler); + resolve(event); + }, delay); + }; + + window.addEventListener("scroll", scrollHandler); + window.scroll({ left, top, behavior: 'smooth' }); + }); +} + +/** + * Exported utility functions + */ + +async function scrollToTarget(target) { + const { x, y, w, h } = getElementCoord(target); + const { W, H } = getWindowSize(); + + let scroll = false; + let left, top; + + if (x < 0 || x + w > W) { + scroll = true; + left = x; + } + + if (y < 0 || y + h > H) { + scroll = true; + top = y; + } + + if (scroll) { + await scrollTo({ top, left }); + } +} + +function setSpotlightPosition(target, spotlight) { + const { x, y, w, h } = getElementCoord(target); + const { sX, sY } = getWindowScroll(); + + Object.assign(spotlight.style, { + left: `${sX + x - spotPadding}px`, + top: `${sY + y - spotPadding}px`, + width: `${w + 2 * spotPadding}px`, + height: `${h + 2 * spotPadding}px` + }); +} + +function setTooltipPosition(target, tooltip) { + const { x, y, w, h } = getElementCoord(target); + const { w: wt, h: ht } = getElementCoord(tooltip); + const { W, H } = getWindowSize(); + const { sX, sY } = getWindowScroll(); + + let left = sX + x + w + spotPadding + tooltipGap; + if (left + wt + borderGap > W) { + left = sX + x - wt - spotPadding - tooltipGap; + } + + let top = sY + y; + if (top + ht + borderGap > H) { + top = sY + y + h - ht; + } + + Object.assign(tooltip.style, { + left: `${left}px`, + top: `${top}px` + }); +} + +export { + scrollToTarget, + setSpotlightPosition, + setTooltipPosition +}; \ No newline at end of file diff --git a/src/BIMDataComponents/index.js b/src/BIMDataComponents/index.js index 73b6404e..24c24d1d 100644 --- a/src/BIMDataComponents/index.js +++ b/src/BIMDataComponents/index.js @@ -33,3 +33,4 @@ export { default as BIMDataTextbox } from "./BIMDataTextbox/BIMDataTextbox.vue"; export { default as BIMDataToggle } from "./BIMDataToggle/BIMDataToggle.vue"; export { default as BIMDataTooltip } from "./BIMDataTooltip/BIMDataTooltip.vue"; export { default as BIMDataIcons } from "./BIMDataIcon/BIMDataLibraryIcons/index.js"; +export { default as BIMDataGuidedTour } from "./BIMDataGuidedTour/BIMDataGuidedTour.vue"; diff --git a/src/store.js b/src/store.js index 8f1e26c6..9c3a3449 100644 --- a/src/store.js +++ b/src/store.js @@ -282,6 +282,13 @@ export default new Vuex.Store({ text: "Tooltips are used to show contextual information on hover.", btn: "View tooltips", }, + { + path: "guided-tour", + img: require("./web/assets/img/icon-list.svg"), + title: "GuidedTour", + text: "On-boarding.", + btn: "View on-boarding", + }, ], }, "Smart Components": { diff --git a/src/web/router/router.js b/src/web/router/router.js index 166254ff..16bc6062 100644 --- a/src/web/router/router.js +++ b/src/web/router/router.js @@ -44,6 +44,7 @@ import Textarea from "../views/Components/Textarea/Textarea.vue"; import Textbox from "../views/Components/Textbox/Textbox.vue"; import Toggle from "../views/Components/Toggle/Toggle.vue"; import Tooltip from "../views/Components/Tooltip/Tooltip.vue"; +import GuidedTour from "../views/Components/GuidedTour/GuidedTour.vue"; // Smart Components views import FileManager from "../views/SmartComponents/FileManager/FileManager.vue"; @@ -257,6 +258,11 @@ const routes = [ name: "tooltips", component: Tooltip, }, + { + path: "guided-tour", + name: "guided-tour", + component: GuidedTour, + }, ], }, { diff --git a/src/web/views/Components/GuidedTour/GuidedTour.vue b/src/web/views/Components/GuidedTour/GuidedTour.vue new file mode 100644 index 00000000..6b0f019d --- /dev/null +++ b/src/web/views/Components/GuidedTour/GuidedTour.vue @@ -0,0 +1,185 @@ + + + + + diff --git a/src/web/views/Components/GuidedTour/steps.js b/src/web/views/Components/GuidedTour/steps.js new file mode 100644 index 00000000..bdae2d26 --- /dev/null +++ b/src/web/views/Components/GuidedTour/steps.js @@ -0,0 +1,15 @@ +const steps = [ + { + target: "[data-guide=step-1]", + title: "Step 1", + content: + "Great, you are now on the IFC list, click on the view button to display the IFC", + }, + { + target: "[data-guide=step-2]", + title: "Step 2", + content: "here is the IFC, click on next to continue", + }, +]; + +export default steps; From f4e2b7b83820cb6766712cc6b6c37fc4e2c840d4 Mon Sep 17 00:00:00 2001 From: Paul Vilalta Date: Thu, 24 Feb 2022 08:33:49 +0100 Subject: [PATCH 02/24] WIP implementation of a specific component --- .../BIMDataGuidedTour/BIMDataGuidedTour.vue | 64 +++++++++---------- .../BIMDataGuidedTour/_BIMDataGuidedTour.scss | 11 +++- 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/src/BIMDataComponents/BIMDataGuidedTour/BIMDataGuidedTour.vue b/src/BIMDataComponents/BIMDataGuidedTour/BIMDataGuidedTour.vue index f4e29899..1aab85f6 100644 --- a/src/BIMDataComponents/BIMDataGuidedTour/BIMDataGuidedTour.vue +++ b/src/BIMDataComponents/BIMDataGuidedTour/BIMDataGuidedTour.vue @@ -1,5 +1,11 @@