|
| 1 | +import React, { useState } from 'react' |
| 2 | +import { WithTooltip } from 'storybook/internal/components' |
| 3 | +import { FORCE_RE_RENDER } from 'storybook/internal/core-events' |
| 4 | +import { addons, useGlobals } from 'storybook/manager-api' |
| 5 | + |
| 6 | +const AngularSVG = `<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" |
| 7 | + viewBox="0 0 250 250" style="enable-background:new 0 0 250 250;" xml:space="preserve"> |
| 8 | +<style type="text/css"> |
| 9 | + .st0{fill:#DD0031;} |
| 10 | + .st1{fill:#C3002F;} |
| 11 | + .st2{fill:#FFFFFF;} |
| 12 | +</style> |
| 13 | +<g> |
| 14 | + <polygon class="st0" points="125,30 125,30 125,30 31.9,63.2 46.1,186.3 125,230 125,230 125,230 203.9,186.3 218.1,63.2 "/> |
| 15 | + <polygon class="st1" points="125,30 125,52.2 125,52.1 125,153.4 125,153.4 125,230 125,230 203.9,186.3 218.1,63.2 125,30 "/> |
| 16 | + <path class="st2" d="M125,52.1L66.8,182.6h0h21.7h0l11.7-29.2h49.4l11.7,29.2h0h21.7h0L125,52.1L125,52.1L125,52.1L125,52.1 |
| 17 | + L125,52.1z M142,135.4H108l17-40.9L142,135.4z"/> |
| 18 | +</g> |
| 19 | +</svg> |
| 20 | +` |
| 21 | +const ReactSVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="-11.5 -10.23174 23 20.46348"> |
| 22 | + <title>React Logo</title> |
| 23 | + <circle cx="0" cy="0" r="2.05" fill="#61dafb"/> |
| 24 | + <g stroke="#61dafb" stroke-width="1" fill="none"> |
| 25 | + <ellipse rx="11" ry="4.2"/> |
| 26 | + <ellipse rx="11" ry="4.2" transform="rotate(60)"/> |
| 27 | + <ellipse rx="11" ry="4.2" transform="rotate(120)"/> |
| 28 | + </g> |
| 29 | +</svg>` |
| 30 | + |
| 31 | +const JavaScriptSVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 630 630"> |
| 32 | +<rect width="630" height="630" fill="#f7df1e"/> |
| 33 | +<path d="m423.2 492.19c12.69 20.72 29.2 35.95 58.4 35.95 24.53 0 40.2-12.26 40.2-29.2 0-20.3-16.1-27.49-43.1-39.3l-14.8-6.35c-42.72-18.2-71.1-41-71.1-89.2 0-44.4 33.83-78.2 86.7-78.2 37.64 0 64.7 13.1 84.2 47.4l-46.1 29.6c-10.15-18.2-21.1-25.37-38.1-25.37-17.34 0-28.33 11-28.33 25.37 0 17.76 11 24.95 36.4 35.95l14.8 6.34c50.3 21.57 78.7 43.56 78.7 93 0 53.3-41.87 82.5-98.1 82.5-54.98 0-90.5-26.2-107.88-60.54zm-209.13 5.13c9.3 16.5 17.76 30.45 38.1 30.45 19.45 0 31.72-7.61 31.72-37.2v-201.3h59.2v202.1c0 61.3-35.94 89.2-88.4 89.2-47.4 0-74.85-24.53-88.81-54.075z"/> |
| 34 | +</svg>` |
| 35 | + |
| 36 | +const SvgIcons = { |
| 37 | + angular: AngularSVG, |
| 38 | + react: ReactSVG, |
| 39 | + html: JavaScriptSVG, |
| 40 | +} as any |
| 41 | + |
| 42 | +const labels = { |
| 43 | + angular: 'Angular', |
| 44 | + html: 'HTML & JS', |
| 45 | + react: 'React', |
| 46 | +} as any |
| 47 | + |
| 48 | +const frameworks = ['angular', 'html', 'react'] |
| 49 | + |
| 50 | +const LOCAL_STORE_ID = 'bal-docs-framework' |
| 51 | + |
| 52 | +const usePersisted = (initialValue: string) => { |
| 53 | + const [storedValue, setStoredValue] = useState(() => { |
| 54 | + try { |
| 55 | + const item = window.localStorage.getItem(LOCAL_STORE_ID) |
| 56 | + const storedFramework = item ? JSON.parse(item) : initialValue |
| 57 | + return frameworks.includes(storedFramework) ? storedFramework : undefined |
| 58 | + } catch (error) { |
| 59 | + return initialValue |
| 60 | + } |
| 61 | + }) |
| 62 | + |
| 63 | + const setValue = (value: string) => { |
| 64 | + const newFramework = frameworks.includes(value) ? value : 'angular' |
| 65 | + setStoredValue(newFramework) |
| 66 | + window.localStorage.setItem(LOCAL_STORE_ID, JSON.stringify(newFramework)) |
| 67 | + } |
| 68 | + |
| 69 | + return [storedValue, setValue] |
| 70 | +} |
| 71 | + |
| 72 | +export const registerFramework = (): React.FC => { |
| 73 | + const [persistedFramework, updatePersisted] = usePersisted('angular') |
| 74 | + const [globals, updateGlobals] = useGlobals() |
| 75 | + let { framework } = globals |
| 76 | + |
| 77 | + const urlSearchParams = new URLSearchParams(window.location.search) |
| 78 | + const params = Object.fromEntries(urlSearchParams.entries()) |
| 79 | + let paramFramework = params.globals?.replace('framework:', '') |
| 80 | + |
| 81 | + paramFramework = frameworks.includes(paramFramework) ? paramFramework : '' |
| 82 | + |
| 83 | + React.useEffect(() => { |
| 84 | + if (!paramFramework && framework !== persistedFramework) { |
| 85 | + updateGlobals({ ...globals, framework: persistedFramework }) |
| 86 | + addons.getChannel().emit(FORCE_RE_RENDER) |
| 87 | + } |
| 88 | + }, [framework, persistedFramework]) |
| 89 | + |
| 90 | + const iframe = document.getElementById('storybook-preview-iframe') as HTMLIFrameElement |
| 91 | + if (iframe && framework) { |
| 92 | + const iframeDocument = iframe.contentDocument || iframe.contentWindow?.document |
| 93 | + if (iframeDocument) { |
| 94 | + const body = iframeDocument.querySelector('body') |
| 95 | + if (body) { |
| 96 | + body.classList.add(`my-framework-${framework}`) |
| 97 | + } |
| 98 | + } |
| 99 | + } |
| 100 | + |
| 101 | + return ( |
| 102 | + <div className="my-framework"> |
| 103 | + <WithTooltip |
| 104 | + placement="top" |
| 105 | + trigger="click" |
| 106 | + closeOnOutsideClick |
| 107 | + tooltip={({ onHide }) => { |
| 108 | + const handleItemClick = value => { |
| 109 | + framework = value |
| 110 | + updatePersisted(framework) |
| 111 | + updateGlobals({ ...globals, framework }) |
| 112 | + onHide() |
| 113 | + } |
| 114 | + |
| 115 | + const isActive = f => (f === framework ? ` my-framework__tooltip__item--active` : '') |
| 116 | + |
| 117 | + return ( |
| 118 | + <div className="my-framework__tooltip"> |
| 119 | + <a |
| 120 | + id="angular" |
| 121 | + className={`my-framework__tooltip__item${isActive('angular')}`} |
| 122 | + onClick={() => handleItemClick('angular')} |
| 123 | + > |
| 124 | + <div className="my-framework__button__icon" dangerouslySetInnerHTML={{ __html: AngularSVG }}></div> |
| 125 | + Angular |
| 126 | + </a> |
| 127 | + <a |
| 128 | + id="html" |
| 129 | + className={`my-framework__tooltip__item${isActive('html')}`} |
| 130 | + onClick={() => handleItemClick('html')} |
| 131 | + > |
| 132 | + <div className="my-framework__button__icon" dangerouslySetInnerHTML={{ __html: JavaScriptSVG }}></div> |
| 133 | + HTML & JS |
| 134 | + </a> |
| 135 | + <a |
| 136 | + id="react" |
| 137 | + className={`my-framework__tooltip__item${isActive('react')}`} |
| 138 | + onClick={() => handleItemClick('react')} |
| 139 | + > |
| 140 | + <div className="my-framework__button__icon" dangerouslySetInnerHTML={{ __html: ReactSVG }}></div> |
| 141 | + React |
| 142 | + </a> |
| 143 | + </div> |
| 144 | + ) |
| 145 | + }} |
| 146 | + > |
| 147 | + <button className="my-framework__button" title="Integration technology"> |
| 148 | + <span className="my-framework__button__label">Framework:</span> |
| 149 | + <div |
| 150 | + className="my-framework__button__icon" |
| 151 | + dangerouslySetInnerHTML={{ __html: SvgIcons[globals.framework] }} |
| 152 | + ></div> |
| 153 | + {labels[globals.framework]} |
| 154 | + </button> |
| 155 | + </WithTooltip> |
| 156 | + </div> |
| 157 | + ) |
| 158 | +} |
0 commit comments