From 270469f3b65f1032d79f1e931069d0f4be25145e Mon Sep 17 00:00:00 2001 From: JohannesDoberer Date: Thu, 19 Mar 2020 17:44:04 +0100 Subject: [PATCH] Release v1 (#1169) --- .travis.yml | 54 +++--- client/luigi-client.d.ts | 165 ++++++++++++------ core/src/core-api/auth.js | 6 + core/src/main.js | 4 + core/src/navigation/ContextSwitcher.html | 7 +- core/src/navigation/services/navigation.js | 19 +- core/src/services/iframe.js | 38 ++-- .../utilities/helpers/navigation-helpers.js | 4 +- core/test/services/iframe.spec.js | 119 ++++++++++++- core/test/services/navigation.spec.js | 8 +- core/test/services/routing.spec.js | 2 + docs/advanced-scenarios.md | 150 ++++++++++++++++ docs/application-setup.md | 2 + docs/faq.md | 6 + docs/navigation-configuration.md | 2 + website/docs/.gitignore | 2 +- website/docs/package-lock.json | 14 +- website/docs/public/index.html | 23 --- .../src/luigi-config/extended/settings.js | 1 + website/docs/static/luigi/docsearch.min.css | 2 - website/docs/static/luigi/docsearch.min.js | 2 - website/docs/static/luigi/index.html | 4 +- website/fiddle/package.json | 2 +- 23 files changed, 490 insertions(+), 146 deletions(-) create mode 100644 docs/advanced-scenarios.md delete mode 100644 website/docs/public/index.html delete mode 100644 website/docs/static/luigi/docsearch.min.css delete mode 100644 website/docs/static/luigi/docsearch.min.js diff --git a/.travis.yml b/.travis.yml index 1d6f5071a3..ffc6bbc203 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +version: ~> 1.0 # enables build config validation beta feature: https://docs.travis-ci.com/user/build-config-validation language: node_js node_js: - '11.14' #TODO: change it to 'node' once Travis supports the newest version @@ -14,6 +15,29 @@ cache: notifications: slack: secure: wiOchVgoiKNUikjCeIhsuv3Y5jolFud6ercdZdO3+JigQqkmoAC95f2jKTYEdplJWyJr8JY6z0+UJNceHyr5YJx/l1wpm6Zq+3H4dq/V0yUXH1uhw2eaTzweX2vjfw0bzewEdc1CGl4kSfdyTajnbKgt+mtnrAV9lPqiXYSOOJTu5BCFomi7u2GhAz1JjBK6P4Ar2jXm+oc81nYjj40P8bZFlA7Rjm0hNN42MMkKvnD8OHztL3EwezKHXlQW7fN4eOrhPOMbT3NRU72g9Nir+lfoKJlch1zPoHXQ7DRp41uGyHV/qfvfRLzXwxftZK6kdvzMe0eI0i1aIm0R6AE0bLphB2o/klJzyQpqSiQgETTH0qJN+3px2kddGrw7Me+UNC/1zZrt1MJfWf0h6WjXTsPDJf3ajLsn8OoIeBTRZbAb9as3UWQZcknuuMf8oGUzZkrZNMWYqo3py0+qWm4wSbWXWVUCgVAIYA6oEADwq37z59HySGkyHI4gEPzxsYODfCuvO4pJX1h4vEyH7w2IigwhDAPq5G4Vdez9ZFS96p/LSCF+iH8yWMy294u8wk5ofWUutFFv0JC45LQBXu51a2TeQkalwVn4DiuxxHE6jZieREw75YFjra4jpWsyziWkzshGT75NCra6cKAbNa4T8n6Pm3ogjEPeeWAO3psgSdo= + + +integration-testing: &integration-testing + script: + - export CYPRESS_CACHE_FOLDER=$TRAVIS_BUILD_DIR/cypress-binary-cache + - ls $CYPRESS_CACHE_FOLDER + - bash ./test/e2e.sh + addons: + apt: + packages: + # Ubuntu 16+ does not install this dependency by default, so we need to install it ourselves + - libgconf-2-4 + #- chromium-browser=79.0.3945.130-0ubuntu0.16.04.1 # update also if you upgrade from Xenial + chrome: beta + before_install: + #- chromium-browser --headless --disable-gpu --remote-debugging-port=9222 http://localhost & + - google-chrome-beta --headless --disable-gpu --remote-debugging-port=9222 http://localhost & + before_cache: + - rm -rf ~/.npm/_logs + - rm -rf ~/.npm/_cacache + - rm -rf ~/.cache/Cypress/cy/production/browsers #it is individual for each run anyway so caching it slows down the job + + jobs: include: - stage: 'Precache & Unit Tests' @@ -72,28 +96,14 @@ jobs: - rm -rf ~/.npm/_cacache # ----- INTEGRATION ----- - - &integration-testing - name: 'Integration Testing' - script: - - export CYPRESS_CACHE_FOLDER=$TRAVIS_BUILD_DIR/cypress-binary-cache - - ls $CYPRESS_CACHE_FOLDER - - bash ./test/e2e.sh - addons: - apt: - packages: - # Ubuntu 16+ does not install this dependency by default, so we need to install it ourselves - - libgconf-2-4 - #- chromium-browser=79.0.3945.130-0ubuntu0.16.04.1 # update also if you upgrade from Xenial - chrome: beta - before_install: - #- chromium-browser --headless --disable-gpu --remote-debugging-port=9222 http://localhost & - - google-chrome-beta --headless --disable-gpu --remote-debugging-port=9222 http://localhost & - before_cache: - - rm -rf ~/.npm/_logs - - rm -rf ~/.npm/_cacache - - rm -rf ~/.cache/Cypress/cy/production/browsers #it is individual for each run anyway so caching it slows down the job - - <<: *integration-testing - - <<: *integration-testing + - name: 'Integration Testing 1' + <<: *integration-testing + + - name: 'Integration Testing 2' + <<: *integration-testing + + - name: 'Integration Testing 3' + <<: *integration-testing # ----- NPM PUBLISH ----- - stage: 'Publish' diff --git a/client/luigi-client.d.ts b/client/luigi-client.d.ts index bae28f6cf9..b29e3da5cc 100644 --- a/client/luigi-client.d.ts +++ b/client/luigi-client.d.ts @@ -78,95 +78,124 @@ export declare interface PathParams { export declare interface UxManager { /** * Adds a backdrop to block the top and side navigation. It is based on the Fundamental UI Modal, which you can use in your micro frontend to achieve the same behavior. + * @memberof uxManager */ addBackdrop: () => void; /** * Removes the backdrop. + * @memberof uxManager */ removeBackdrop: () => void; /** - * Adds a backdrop with a loading indicator for the micro frontend frame. This overrides the {@link navigation-configuration.md#nodes loadingIndicator.enabled} setting. + * Adds a backdrop with a loading indicator for the micro frontend frame. This overrides the {@link navigation-parameters-reference.md#node-parameters loadingIndicator.enabled} setting. + * @memberof uxManager */ showLoadingIndicator: () => void; /** - * Removes the loading indicator. Use it after calling {@link #showLoadingIndicator showLoadingIndicator()} or to hide the indicator when you use the {@link navigation-configuration.md#nodes loadingIndicator.hideAutomatically: false} node configuration. + * Removes the loading indicator. Use it after calling {@link #showLoadingIndicator showLoadingIndicator()} or to hide the indicator when you use the {@link navigation-parameters-reference.md#node-parameters loadingIndicator.hideAutomatically: false} node configuration. + * @memberof uxManager */ hideLoadingIndicator: () => void; /** * This method informs the main application that there are unsaved changes in the current view in the iframe. For example, that can be a view with form fields which were edited but not submitted. * @param {boolean} isDirty indicates if there are any unsaved changes on the current page or in the component + * @memberof uxManager */ setDirtyStatus: (isDirty: boolean) => void; /** * Shows an alert. + * @memberof uxManager * @param {Object} settings the settings for the alert - * @param {string} settings.text the content of the alert. To add a link to the content, you have to set up the link in the `links` object. The key(s) in the `links` object must be used in the text to reference the links, wrapped in curly brackets with no spaces. If you don't specify any text, the alert is not displayed. - * @param {('info'|'success'|'warning'|'error')} settings.type sets the type of the alert + * @param {string} settings.text the content of the alert. To add a link to the content, you have to set up the link in the `links` object. The key(s) in the `links` object must be used in the text to reference the links, wrapped in curly brackets with no spaces. If you don't specify any text, the alert is not displayed + * @param {('info'|'success'|'warning'|'error')} settings.type sets the type of alert * @param {Object} settings.links provides links data - * @param {Object} settings.links.LINK_KEY object containing the data for a particular link. To properly render the link in the alert message refer to the description of the **settings.text** parameter. + * @param {Object} settings.links.LINK_KEY object containing the data for a particular link. To properly render the link in the alert message refer to the description of the **settings.text** parameter * @param {string} settings.links.LINK_KEY.text text which replaces the link identifier in the alert content - * @param {string} settings.links.LINK_KEY.url url to navigate when you click the link. Currently, only internal links are supported in the form of relative or absolute paths. + * @param {string} settings.links.LINK_KEY.url url to navigate when you click the link. Currently, only internal links are supported in the form of relative or absolute paths + * @param {number} settings.closeAfter (optional) time in milliseconds that tells Luigi when to close the Alert automatically. If not provided, the Alert will stay on until closed manually. It has to be greater than `100` * @returns {promise} which is resolved when the alert is dismissed * @example * import LuigiClient from '@luigi-project/client'; * const settings = { - * text: Ut enim ad minim veniam, {goToHome} quis nostrud exercitation ullamco {relativePath} laboris nisi ut aliquip ex ea commodo consequat. - * Duis aute irure dolor {goToOtherProject}, + * text: "Ut enim ad minim veniam, {goToHome} quis nostrud exercitation ullamco {relativePath}. Duis aute irure dolor {goToOtherProject}", * type: 'info', * links: { * goToHome: { text: 'homepage', url: '/overview' }, * goToOtherProject: { text: 'other project', url: '/projects/pr2' }, * relativePath: { text: 'relative hide side nav', url: 'hideSideNav' } - * } + * }, + * closeAfter: 3000 * } * LuigiClient * .uxManager() * .showAlert(settings) * .then(() => { * // Logic to execute when the alert is dismissed - * }); + * }); */ showAlert: (settings: AlertSettings) => Promise; /** * Shows a confirmation modal. - * @param {Object} settings the settings the confirmation modal. If no value is provided for any of the fields, a default value is set for it. - * @param {string} settings.header the content of the modal header - * @param {string} settings.body the content of the modal body - * @param {string} settings.buttonConfirm the label for the modal confirm button - * @param {string} settings.buttonDismiss the label for the modal dismiss button - * @returns {promise} which is resolved when accepting the confirmation modal and rejected when dismissing it. + * @memberof uxManager + * @param {Object} settings the settings of the confirmation modal. If you don't provide any value for any of the fields, a default value is used + * @param {string} [settings.header="Confirmation"] the content of the modal header + * @param {string} [settings.body="Are you sure you want to do this?"] the content of the modal body + * @param {string} [settings.buttonConfirm="Yes"] the label for the modal confirm button + * @param {string} [settings.buttonDismiss="No"] the label for the modal dismiss button + * @returns {promise} which is resolved when accepting the confirmation modal and rejected when dismissing it + * @example + * import LuigiClient from '@kyma-project/luigi-client'; + * const settings = { + * header: "Confirmation", + * body: "Are you sure you want to do this?", + * buttonConfirm: "Yes", + * buttonDismiss: "No" + * } + * LuigiClient + * .uxManager() + * .showConfirmationModal(settings) + * .then(() => { + * // Logic to execute when the confirmation modal is dismissed + * }); */ showConfirmationModal: (settings: ConfirmationModalSettings) => Promise; /** * Gets the current locale. * @returns {string} current locale + * @memberof uxManager */ getCurrentLocale: () => string; /** * Sets current locale to the specified one. + * + * **NOTE:** this must be explicitly allowed on the navigation node level by setting `clientPermissions.changeCurrentLocale` to `true`. (See {@link navigation-parameters-reference.md Node parameters}.) + * * @param {string} locale locale to be set as the current locale + * @memberof uxManager */ setCurrentLocale: (locale: string) => void; /** * Checks if the current micro frontend is displayed inside a split view - * @since 0.6.0 * @returns {boolean} indicating if it is loaded inside a split view + * @memberof uxManager + * @since 0.6.0 */ isSplitView: () => boolean; /** * Checks if the current micro frontend is displayed inside a modal - * @since 0.6.0 * @returns {boolean} indicating if it is loaded inside a modal + * @memberof uxManager + * @since 0.6.0 */ isModal: () => boolean; } @@ -174,7 +203,8 @@ export declare interface UxManager { export declare interface LinkManager { /** * Sets the current navigation context which is then used by the `navigate` function. This has to be a parent navigation context, it is not possible to use the child navigation contexts. - * @returns {linkManager} link manager instance. + * @memberof linkManager + * @returns {linkManager} link manager instance * @example * LuigiClient.linkManager().fromClosestContext().navigate('/users/groups/stakeholders') */ @@ -182,8 +212,9 @@ export declare interface LinkManager { /** * Sets the current navigation context to that of a specific parent node which has the {@link navigation-configuration.md navigationContext} field declared in the navigation configuration. This navigation context is then used by the `navigate` function. + * @memberof linkManager * @param {string} navigationContext - * @returns {linkManager} link manager instance. + * @returns {linkManager} link manager instance * @example * LuigiClient.linkManager().fromContext('project').navigate('/settings') */ @@ -191,7 +222,8 @@ export declare interface LinkManager { /** * Discards the active view and navigates back to the last visited view. Works with preserved views, and also acts as the substitute of the browser **back** button. **goBackContext** is only available when using preserved views. - * @param {any} goBackValue data that is passed in the **goBackContext** field to the last visited view when using preserved views. + * @memberof linkManager + * @param {any} goBackValue data that is passed in the **goBackContext** field to the last visited view when using preserved views * @example * LuigiClient.linkManager().goBack({ foo: 'bar' }); * LuigiClient.linkManager().goBack(true); @@ -200,18 +232,24 @@ export declare interface LinkManager { /** * Checks if there is one or more preserved views. You can use it to show a **back** button. - * @returns {boolean} indicating if there is a preserved view you can return to. + * @memberof linkManager + * @returns {boolean} indicating if there is a preserved view you can return to */ hasBack: () => boolean; /** * Navigates to the given path in the application hosted by Luigi. It contains either a full absolute path or a relative path without a leading slash that uses the active route as a base. This is the standard navigation. + * @memberof linkManager * @param {string} path path to be navigated to * @param {string} sessionId current Luigi **sessionId** - * @param {boolean} preserveView Preserve a view by setting it to `true`. It keeps the current view opened in the background and opens the new route in a new frame. Use the {@link #goBack goBack()} function to navigate back. You can use this feature across different levels. Preserved views are discarded as soon as the standard {@link #navigate navigate()} function is used instead of {@link #goBack goBack()}. - * @param {Object} modalSettings opens a view in a modal. Use these settings to configure the modal's title and size. - * @param {string} modalSettings.title modal title. By default, it is the node label. If there is no label, it is left empty. - * @param {('l'|'m'|'s')} [modalSettings.size=l] size of the modal (optional, default `l`) + * @param {boolean} preserveView preserve a view by setting it to `true`. It keeps the current view opened in the background and opens the new route in a new frame. Use the {@link #goBack goBack()} function to navigate back. You can use this feature across different levels. Preserved views are discarded as soon as you use the standard {@link #navigate navigate()} function instead of {@link #goBack goBack()} + * @param {Object} modalSettings opens a view in a modal. Use these settings to configure the modal's title and size + * @param {string} modalSettings.title modal title. By default, it is the node label. If there is no label, it is left empty + * @param {('l'|'m'|'s')} [modalSettings.size="l"] size of the modal + * @param {Object} splitViewSettings opens a view in a split view. Use these settings to configure the split view's behaviour + * @param {string} splitViewSettings.title split view title. By default, it is the node label. If there is no label, it is left empty + * @param {number} [splitViewSettings.size=40] height of the split view in percent + * @param {boolean} [splitViewSettings.collapsed=false] creates split view but leaves it closed initially * @example * LuigiClient.linkManager().navigate('/overview') * LuigiClient.linkManager().navigate('users/groups/stakeholders') @@ -224,13 +262,15 @@ export declare interface LinkManager { modalSettings?: ModalSettings ) => void; + /** @lends linkManager */ /** * Checks if the path you can navigate to exists in the main application. For example, you can use this helper method conditionally to display a DOM element like a button. + * @memberof linkManager * @param {string} path path which existence you want to check - * @returns {promise} A promise which resolves to a Boolean variable specifying whether the path exists or not. + * @returns {promise} a promise which resolves to a Boolean variable specifying whether the path exists or not * @example * let pathExists; - * this.luigiClient + * LuigiClient * .linkManager() * .pathExists('projects/pr2') * .then( @@ -241,8 +281,9 @@ export declare interface LinkManager { /** * Sends node parameters to the route. The parameters are used by the `navigate` function. Use it optionally in combination with any of the navigation functions and receive it as part of the context object in Luigi Client. + * @memberof linkManager * @param {Object} nodeParams - * @returns {linkManager} link manager instance. + * @returns {linkManager} link manager instance * @example * LuigiClient.linkManager().withParams({foo: "bar"}).navigate("path") * @@ -253,11 +294,11 @@ export declare interface LinkManager { /** * Opens a view in a modal. You can specify the modal's title and size. If you don't specify the title, it is the node label. If there is no node label, the title remains empty. The default size of the modal is `l`, which means 80%. You can also use `m` (60%) and `s` (40%) to set the modal size. Optionally, use it in combination with any of the navigation functions. + * @memberof linkManager * @param {string} path navigation path - * @param {Object} modalSettings opens a view in a modal. Use these settings to configure the modal's title and size. - * @param {string} modalSettings.title modal title. By default, it is the node label. If there is no label, it is left empty. - * @param {('l'|'m'|'s')} [modalSettings.size=l] size of the modal (optional, default `l`) - * @since 0.4.11 + * @param {Object} [modalSettings] opens a view in a modal. Use these settings to configure the modal's title and size + * @param {string} modalSettings.title modal title. By default, it is the node label. If there is no label, it is left empty + * @param {('l'|'m'|'s')} [modalSettings.size="l"] size of the modal * @example * LuigiClient.linkManager().openAsModal('projects/pr1/users', {title:'Users', size:'m'}); */ @@ -296,7 +337,7 @@ export declare interface LinkManager { /** * Registers a listener called with the context object and the Luigi Core domain as soon as Luigi is instantiated. Defer your application bootstrap if you depend on authentication data coming from Luigi. - * @param {function} initFn the function that is called once Luigi is initialized, receives current context and origin as parameters. + * @param {Lifecycle~initListenerCallback} initFn the function that is called once Luigi is initialized, receives current context and origin as parameters * @memberof Lifecycle */ export function addInitListener( @@ -306,16 +347,22 @@ export type addInitListener = ( initFn: (context: Context, origin?: string) => void ) => number; +/** + * Callback of the addInitListener + * @callback Lifecycle~initListenerCallback + * @param {Object} context current context data + * @param {string} origin Luigi Core URL + */ /** * Removes an init listener. - * @param {string} id the id that was returned by the `addInitListener` function + * @param {string} id the id that was returned by the `addInitListener` function. * @memberof Lifecycle */ export function removeInitListener(id: number): boolean; export type removeInitListener = (id: number) => boolean; /** - * Registers a listener called with the context object upon any navigation change. + * Registers a listener called with the context object when the URL is changed. For example, you can use this when changing environments in a context switcher in order for the micro frontend to do an API call to the environment picked. * @param {function} contextUpdatedFn the listener function called each time Luigi context changes * @memberof Lifecycle */ @@ -335,8 +382,13 @@ export function removeContextUpdateListener(id: string): boolean; export type removeContextUpdateListener = (id: string) => boolean; /** - * Registers a listener called upon microfrontend inactivity. Usually happens when micro frontends get moved to the background when using preserve view, loading new view groups or using preload. - * @param {function} inactiveFn the listener function called each time the micro frontend turns into an inactive state + * Registers a listener called upon micro frontend inactivity. This happens when a new micro frontend gets shown while keeping the old one cached. + * Gets called when: + * - navigating with **preserveView** + * - navigating from or to a **viewGroup** + * + * Does not get called when navigating normally, or when `openAsModal` or `openAsSplitView` are used. + * @param {function} inactiveFn the listener function called each time a micro frontend turns into an inactive state * @memberof Lifecycle */ export function addInactiveListener(inactiveFn: () => void): string; @@ -353,8 +405,9 @@ export type removeInactiveListener = (id: string) => boolean; /** * Registers a listener called when the micro frontend receives a custom message. * @param {string} customMessageId the custom message id - * @param {Lifecycle~customMessageListenerCallback} customMessageListener the function that is called when the micro frontend receives the corresponding event. + * @param {Lifecycle~customMessageListenerCallback} customMessageListener the function that is called when the micro frontend receives the corresponding event * @memberof Lifecycle + * @since 0.6.2 */ export function addCustomMessageListener( customMessageId: string, @@ -371,25 +424,28 @@ export type addCustomMessageListener = ( * @param {Object} customMessage custom message object * @param {string} customMessage.id message id * @param {*} customMessage.MY_DATA_FIELD any other message data field - * @param {string} listenerId custom message listener id to be used for unsubscribing + * @param {string} listenerId custom message listener id to be used for unsubscription */ /** * Removes a custom message listener. - * @param {string} listenerId the id that was returned by the `addInitListener` function + * @param {string} id the id that was returned by the `addInitListener` function * @memberof Lifecycle + * @since 0.6.2 */ export function removeCustomMessageListener(id: string): boolean; export type removeCustomMessageListener = (id: string) => boolean; /** - * @returns {string} the authorization token + * Returns the currently valid access token. + * @returns {string} current access token + * @memberof Lifecycle */ export function getToken(): AuthData['accessToken']; export type getToken = () => AuthData['accessToken']; /** * Returns the context object. Typically it is not required as the {@link #addContextUpdateListener addContextUpdateListener()} receives the same values. - * @returns {Object} current context data. + * @returns {Object} current context data * @memberof Lifecycle */ export function getEventData(): Context; @@ -397,7 +453,7 @@ export type getEventData = () => Context; /** * Returns the context object. It is an alias function for getEventData(). - * @returns {Object} current context data. + * @returns {Object} current context data * @memberof Lifecycle */ export function getContext(): Context; @@ -405,10 +461,10 @@ export type getContext = () => Context; /** * Returns the node parameters of the active URL. - * Node parameters are defined like URL query parameters but with a specific prefix allowing Luigi to pass them to the micro frontend view. The default prefix is **~** and you can use it in the following way: `https://my.luigi.app/home/products?~sort=asc~page=3`. + * Node parameters are defined like URL query parameters but with a specific prefix allowing Luigi to pass them to the micro frontend view. The default prefix is **~** and you can use it in the following way: `https://my.luigi.app/home/products?~sort=asc~page=3`. * * > **NOTE:** some special characters (`<`, `>`, `"`, `'`, `/`) in node parameters are HTML-encoded. - * @returns {Object} node parameters, where the object property name is the node parameter name without the prefix, and its value is the value of the node parameter. For example `{sort: 'asc', page: 3}`. + * @returns {Object} node parameters, where the object property name is the node parameter name without the prefix, and its value is the value of the node parameter. For example `{sort: 'asc', page: 3}` * @memberof Lifecycle */ export function getNodeParams(): NodeParams; @@ -420,7 +476,7 @@ export type getNodeParams = () => NodeParams; * All path parameters in the current navigation path (as defined by the active URL) are returned. * * > **NOTE:** some special characters (`<`, `>`, `"`, `'`, `/`) in path parameters are HTML-encoded. - * @returns {Object} path parameters, where the object property name is the path parameter name without the prefix, and its value is the actual value of the path parameter. For example ` {productId: 1234, ...}`. + * @returns {Object} path parameters, where the object property name is the path parameter name without the prefix, and its value is the actual value of the path parameter. For example ` {productId: 1234, ...}` * @memberof Lifecycle */ export function getPathParams(): PathParams; @@ -428,7 +484,7 @@ export type getPathParams = () => PathParams; /** * Returns the current client permissions as specified in the navigation node or an empty object. For details, see [Node parameters](navigation-parameters-reference.md). - * @returns {Object} client permissions as specified in the navigation node. + * @returns {Object} client permissions as specified in the navigation node * @memberof Lifecycle */ export function getClientPermissions(): ClientPermissions; @@ -436,7 +492,7 @@ export type getClientPermissions = () => ClientPermissions; /** * When the micro frontend is not embedded in the Luigi Core application and there is no init handshake you can set the target origin that is used in postMessage function calls by Luigi Client. - * @param {string} targetOrigin target origin. + * @param {string} origin target origin * @memberof Lifecycle * @since 0.7.3 */ @@ -445,30 +501,31 @@ export type setTargetOrigin = (targetOrigin: string) => void; /** * Sends a custom message to the Luigi Core application. - * @param {Object} message an object containing data to be sent to the Luigi Core to further process the custom event. This object is set as an input parameter of the event handler on the Luigi Core side. + * @param {Object} message an object containing data to be sent to the Luigi Core to process it further. This object is set as an input parameter of the custom message listener on the Luigi Core side * @param {string} message.id a string containing the message id * @param {*} message.MY_DATA_FIELD any other message data field * @example * import LuigiClient from '@luigi-project/client'; * LuigiClient.sendCustomMessage({id: 'environment.created', production: false}) * @memberof Lifecycle + * @since 0.6.2 */ export function sendCustomMessage(message: object): void; export type sendCustomMessage = (message: object) => void; /** * The Link Manager allows you to navigate to another route. Use it instead of an internal router to: - - Route inside micro frontends. + - Provide routing inside micro frontends. - Reflect the route. - Keep the navigation state in Luigi. -*/ -/** @name linkManager */ + * @name linkManager + */ export function linkManager(): LinkManager; export type linkManager = () => LinkManager; /** * Use the UX Manager to manage the appearance features in Luigi. + * @name uxManager */ -/** @name uxManager */ export function uxManager(): UxManager; export type uxManager = () => UxManager; diff --git a/core/src/core-api/auth.js b/core/src/core-api/auth.js index fe434c8c0c..d63980ec8e 100644 --- a/core/src/core-api/auth.js +++ b/core/src/core-api/auth.js @@ -66,6 +66,12 @@ class LuigiAuth { * @property {string} idToken - id token, used for renewing authentication */ get store() { + if (!LuigiConfig.initialized) { + console.warn( + 'Luigi Core is not initialized yet. Consider moving your code to the luigiAfterInit lifecycle hook. ' + + 'Documentation: https://docs.luigi-project.io/docs/lifecycle-hooks' + ); + } return { /** * Retrieves the key name that is used to store the auth data. diff --git a/core/src/main.js b/core/src/main.js index 79d678bf49..ee8d7907d6 100644 --- a/core/src/main.js +++ b/core/src/main.js @@ -83,6 +83,10 @@ const configReadyCallback = () => { return app.$$.ctx.pathExists(path); }; + Luigi.pathExists = path => { + return app.$$.ctx.pathExists(path); + }; + Luigi.hasBack = () => { return app.$$.ctx.hasBack(); }; diff --git a/core/src/navigation/ContextSwitcher.html b/core/src/navigation/ContextSwitcher.html index 87bb5b7b57..b7238bc6c1 100644 --- a/core/src/navigation/ContextSwitcher.html +++ b/core/src/navigation/ContextSwitcher.html @@ -98,6 +98,7 @@
import ContextSwitcherNav from './ContextSwitcherNav.html'; import { LuigiConfig } from '../core-api'; import { Routing } from '../services/routing'; + import { NodeDataManagementStorage } from '../services/node-data-management'; import { IframeHelpers, RoutingHelpers, @@ -139,7 +140,7 @@
StateHelpers.doOnStoreChange( store, async () => { - LuigiConfig._configModificationTimestamp = new Date(); + NodeDataManagementStorage.deleteCache(); const contextSwitcherConfig = LuigiConfig.getConfigValue( 'navigation.contextSwitcher' ); @@ -278,7 +279,7 @@
export function goToPath(path) { getUnsavedChangesModalPromise().then(() => { - LuigiConfig._configModificationTimestamp = new Date(); + NodeDataManagementStorage.deleteCache(); Routing.navigateTo(path); }); } @@ -287,7 +288,7 @@
let option = event.detail.option; let selectedOption = event.detail.selectedOption; getUnsavedChangesModalPromise().then(() => { - LuigiConfig._configModificationTimestamp = new Date(); + NodeDataManagementStorage.deleteCache(); if (preserveSubPathOnSwitch && selectedOption) { Routing.navigateTo( ContextSwitcherHelpers.getNodePathFromCurrentPath( diff --git a/core/src/navigation/services/navigation.js b/core/src/navigation/services/navigation.js index 63fde22765..3b342eb13d 100644 --- a/core/src/navigation/services/navigation.js +++ b/core/src/navigation/services/navigation.js @@ -104,6 +104,15 @@ class NavigationClass { return filteredChildren; } + /** + * returns filtered children from cache if present otherwise calculates them. + * */ + async getFilteredChildren(node) { + return NodeDataManagementStorage.hasChildren(node) + ? Navigation.getChildrenFromCache(node) + : await Navigation.getChildren(node); + } + getChildrenFromCache(node) { let data = NodeDataManagementStorage.getChildren(node); return data ? data.filteredChildren : []; @@ -271,10 +280,12 @@ class NavigationClass { Object.assign(newChild, { pathSegment: ':virtualSegment_' + _virtualPathIndex, label: ':virtualSegment_' + _virtualPathIndex, - viewUrl: this.buildVirtualViewUrl( - _virtualViewUrl, - pathParams, - _virtualPathIndex + viewUrl: GenericHelpers.trimTrailingSlash( + this.buildVirtualViewUrl( + _virtualViewUrl, + pathParams, + _virtualPathIndex + ) ), _virtualTree: true, _virtualPathIndex, diff --git a/core/src/services/iframe.js b/core/src/services/iframe.js index c43745bd20..53099eb46b 100644 --- a/core/src/services/iframe.js +++ b/core/src/services/iframe.js @@ -147,6 +147,25 @@ class IframeClass { return newActiveIframe; } + setOkResponseHandler(config, component, node) { + /** + * check if luigi responded + * if not, callback again to replace the iframe + */ + this.timeoutHandle = setTimeout(() => { + if (config.navigateOk) { + config.navigateOk = undefined; + } else { + config.iframe = undefined; + config.isFallbackFrame = true; + console.info( + 'navigate: luigi-client did not respond, using fallback by replacing iframe' + ); + this.navigateIframe(config, component, node); + } + }, this.iframeNavFallbackTimeout); + } + navigateIframe(config, component, node) { clearTimeout(this.timeoutHandle); const componentData = component.get(); @@ -264,6 +283,8 @@ class IframeClass { const message = ['init', JSON.stringify(componentData.context)]; IframeHelpers.sendMessageToIframe(config.iframe, message); }); + } else if (!config.isFallbackFrame) { + this.setOkResponseHandler(config, component, node); } } } else { @@ -290,6 +311,7 @@ class IframeClass { if (withSync) { // default, send navigation event to client IframeHelpers.sendMessageToIframe(config.iframe, message); + this.setOkResponseHandler(config, component, node); } else { // `withoutSync()` used. client navigation was skipped, reset after one-time use. component.set({ isNavigationSyncEnabled: true }); @@ -297,22 +319,6 @@ class IframeClass { // clear goBackContext and reset navigateBack after sending it to the client component.set({ goBackContext: undefined, isNavigateBack: false }); - - /** - * check if luigi responded - * if not, callback again to replace the iframe - */ - this.timeoutHandle = setTimeout(() => { - if (config.navigateOk) { - config.navigateOk = undefined; - } else { - config.iframe = undefined; - console.info( - 'navigate: luigi-client did not respond, using fallback by replacing iframe' - ); - this.navigateIframe(config, component, node); - } - }, this.iframeNavFallbackTimeout); } } } diff --git a/core/src/utilities/helpers/navigation-helpers.js b/core/src/utilities/helpers/navigation-helpers.js index 97e718765b..dd51987200 100644 --- a/core/src/utilities/helpers/navigation-helpers.js +++ b/core/src/utilities/helpers/navigation-helpers.js @@ -2,7 +2,7 @@ import { LuigiAuth, LuigiConfig } from '../../core-api'; import { AuthHelpers } from './'; import { Navigation } from '../../navigation/services/navigation'; - +import { NodeDataManagementStorage } from '../../services/node-data-management'; class NavigationHelpersClass { constructor() { this.EXP_CAT_KEY = 'luigi.preferences.navigation.expandedCategories'; @@ -150,7 +150,7 @@ class NavigationHelpersClass { } async generateTopNavNodes(pathData) { - const rawChildren = await Navigation.getChildrenFromCache(pathData[0]); + const rawChildren = await Navigation.getFilteredChildren(pathData[0]); let selectedNode = null; let visibleNodeCount = 0; let cats = {}; diff --git a/core/test/services/iframe.spec.js b/core/test/services/iframe.spec.js index b35b0b45e9..64e7192f27 100644 --- a/core/test/services/iframe.spec.js +++ b/core/test/services/iframe.spec.js @@ -17,8 +17,18 @@ describe('Iframe', () => { let component; beforeEach(() => { + global['sessionStorage'] = { + getItem: sinon.stub(), + setItem: sinon.stub() + }; + global['localStorage'] = { + getItem: sinon.stub(), + setItem: sinon.stub() + }; clock = sinon.useFakeTimers(); - let lastObj = {}; + let lastObj = { + isNavigationSyncEnabled: true + }; component = { set: obj => { Object.assign(lastObj, obj); @@ -26,6 +36,7 @@ describe('Iframe', () => { get: () => lastObj, prepareInternalData: () => {} }; + sinon.stub(Iframe, 'setOkResponseHandler'); sinon.stub(LuigiConfig, 'getConfigValue').callsFake(); sinon.stub(GenericHelpers); GenericHelpers.getRandomId.returns('abc'); @@ -191,7 +202,6 @@ describe('Iframe', () => { describe('check if luigi respond, if not, callback again to replace the iframe', () => { it('navigate', () => { - const spy = sinon.spy(console, 'info'); sinon.stub(IframeHelpers, 'getMainIframes').callsFake(() => [ { src: 'http://url.com/app.html!#/prevUrl', @@ -216,12 +226,76 @@ describe('Iframe', () => { }); assert.equal(config.iframe.src, 'http://luigi.url.de'); Iframe.navigateIframe(config, component, node); - clock.tick(3000); - assert(spy.called, 'console.info() call'); + + assert(Iframe.setOkResponseHandler.called, 'setOkResponseHandler call'); assert.equal(config.iframe.src, 'http://url.com/app.html!#/prevUrl'); }); }); + // If with setTimeout, async clock does not work. + xdescribe('setOkResponseHandler', () => { + beforeEach(() => { + Iframe.setOkResponseHandler.restore(); + }); + beforeEach(() => { + sinon.restore(); + }); + it('ok', () => { + sinon.stub(Iframe, 'navigateIframe'); + const config = { + navigateOk: true, + iframe: { + src: 'http://luigi.url.de' + } + }; + component.set({ + currentNode: {} + }); + + assert.isTrue(config.navigateOk); + + Iframe.setOkResponseHandler(config, component, node); + clock.tick(3000); + + assert.isUndefined(config.navigateOk); + assert.deepEqual(config, { + navigateOk: undefined, + iframe: { + src: 'http://luigi.url.de' + } + }); + assert( + Iframe.navigateIframe.notCalled, + 'Iframe.navigateIframe not called' + ); + }); + it('not ok', () => { + sinon.stub(Iframe, 'navigateIframe'); + sinon.stub(console, 'info'); + const config = { + navigateOk: undefined, + iframe: { + src: 'http://luigi.url.de' + } + }; + component.set({ + currentNode: {} + }); + + Iframe.setOkResponseHandler(config, component, node); + clock.tick(3000); + + assert.isUndefined(config.navigateOk); + assert.deepEqual(config, { + navigateOk: undefined, + iframe: undefined, + isFallbackFrame: true + }); + assert(console.info.called, 'console.info called'); + assert(Iframe.navigateIframe.called, 'Iframe.navigateIframe called'); + }); + }); + describe('use cached iframe with same viewgroup and change viewUrl', () => { it('navigate', () => { sinon.stub(IframeHelpers, 'getMainIframes').callsFake(() => [ @@ -259,4 +333,41 @@ describe('Iframe', () => { assert.equal(config.iframe.luigi.nextViewUrl, 'http://luigi.url.de/1m'); }); }); + + describe('using withoutSync whould not trigger iframe fallback', () => { + it('navigate', () => { + const spy = sinon.spy(console, 'info'); + spy.resetHistory(); + + sinon.stub(IframeHelpers, 'getMainIframes').callsFake(() => [ + { + src: 'http://url.com/app.html!#/prevUrl', + style: { display: 'block' }, + vg: 'tets1', + luigi: {} + } + ]); + const config = { + iframe: { + src: 'http://luigi.url.de', + vg: 'tets2' + } + }; + component.set({ + viewUrl: 'http://luigi.url.de/1', + viewGroup: 'tets1', + previousNodeValues: { + viewUrl: 'http://luigi.url.desdf/1' + }, + currentNode: {}, + isNavigationSyncEnabled: false + }); + assert.equal(config.iframe.src, 'http://luigi.url.de'); + Iframe.navigateIframe(config, component, node); + clock.tick(3000); + assert(spy.notCalled, 'console.info() call should not apply'); + // assert.equal(config.iframe.src, 'http://luigi.url.de'); + assert.isTrue(component.get().isNavigationSyncEnabled); + }); + }); }); diff --git a/core/test/services/navigation.spec.js b/core/test/services/navigation.spec.js index 4312b9b4a8..febaf02569 100644 --- a/core/test/services/navigation.spec.js +++ b/core/test/services/navigation.spec.js @@ -943,6 +943,8 @@ describe('Navigation', function() { }, index: 3 }; + + // trailing slash is expected, it gets removed later by trimTrailingSlash() before setting viewUrl const expected = 'https://mf.luigi-project.io#!/x/:virtualSegment_1/:virtualSegment_2/:virtualSegment_3/'; @@ -993,7 +995,7 @@ describe('Navigation', function() { _virtualPathIndex: 1, label: ':virtualSegment_1', pathSegment: ':virtualSegment_1', - viewUrl: 'http://mf.luigi-project.io/:virtualSegment_1/', + viewUrl: 'http://mf.luigi-project.io/:virtualSegment_1', _virtualViewUrl: 'http://mf.luigi-project.io' } ] @@ -1010,7 +1012,7 @@ describe('Navigation', function() { label: ':virtualSegment_3', pathSegment: ':virtualSegment_3', viewUrl: - 'http://mf.luigi-project.io/:virtualSegment_2/:virtualSegment_3/', + 'http://mf.luigi-project.io/:virtualSegment_2/:virtualSegment_3', _virtualViewUrl: 'http://mf.luigi-project.io' }; const mockNodeNames = ['foo']; @@ -1029,7 +1031,7 @@ describe('Navigation', function() { label: ':virtualSegment_4', pathSegment: ':virtualSegment_4', viewUrl: - 'http://mf.luigi-project.io/:virtualSegment_1/:virtualSegment_2/:virtualSegment_3/:virtualSegment_4/', + 'http://mf.luigi-project.io/:virtualSegment_1/:virtualSegment_2/:virtualSegment_3/:virtualSegment_4', _virtualViewUrl: 'http://mf.luigi-project.io' } ] diff --git a/core/test/services/routing.spec.js b/core/test/services/routing.spec.js index 14e349aba1..d9837228af 100644 --- a/core/test/services/routing.spec.js +++ b/core/test/services/routing.spec.js @@ -8,6 +8,7 @@ import { GenericHelpers } from '../../src/utilities/helpers'; import { LuigiConfig, LuigiI18N } from '../../src/core-api'; import { Navigation } from '../../src/navigation/services/navigation'; import { NodeDataManagementStorage } from '../../src/services/node-data-management'; +import { Iframe } from '../../src/services/iframe'; describe('Routing', function() { this.retries(1); @@ -167,6 +168,7 @@ describe('Routing', function() { let config; beforeEach(() => { + sinon.stub(Iframe, 'setOkResponseHandler'); const sampleLuigiConfig = { navigation: { nodes: () => [ diff --git a/docs/advanced-scenarios.md b/docs/advanced-scenarios.md new file mode 100644 index 0000000000..60aac33c07 --- /dev/null +++ b/docs/advanced-scenarios.md @@ -0,0 +1,150 @@ + + +# Expert scenarios + +This is a collection of advanced use cases and example implementations. If you are new to Luigi, take a look at our [Getting Started](getting-started.md) section first. + + + +### Use a SPA router and keep Luigi Core URL in sync + +#### Overview + +This example shows you how to keep an existing routing strategy and use an existing micro frontend as drop-in without the need to refactor everything to [`LuigiClient.linkManager()`](https://docs.luigi-project.io/docs/luigi-client-api?section=linkmanager). To update the Luigi Core URL when routing internally with the micro frontend router, without updating the URL on the Luigi Client side, use the `linkManager()` [withoutSync](luigi-client-api.md#withoutsync) method. + + +> **NOTE**: This is a very simple example. For cases like modals or split views, you still require the use of [Luigi Client](luigi-client-api.md). + +#### Steps + +1. Configure the Luigi navigation node: + + +> **NOTE**: To keep the example simple, we use [virtualTree](navigation-parameters-reference.md#virtualtree) to allow any nested navigation, but this is not mandatory. You can always specify the node tree yourself and still use automatic navigation with router events. + +```javascript + { + pathSegment: 'Orders', + label: 'orders', + viewUrl: 'https://orders.microfrontend/', + navigationContext: 'orders', + virtualTree: true + } +``` + +2. Use an Angular Router for navigation. + +Angular provides [Router events](https://angular.io/guide/router#router-events). We are reacting on `NavigationEnd` to update the URL after a successful route change. + +We assume that the whole Angular app is one micro frontend and has its routes declared on the root level: + +```javascript + { path: 'preload', component: PreloadComponent }, + { path: '', component: OrderListComponent }, + { path: ':id', component: OrderComponent }, + { path: ':id/details', component: OrderDetailsComponent }, +``` + +Use this code to implement `luigi-auto-navigation.service.ts`, which is globally imported in our `app.module.ts`: + +```javascript + import { Router, NavigationEnd } from '@angular/router'; + import { Injectable, OnDestroy } from '@angular/core'; + import { Subscription } from 'rxjs'; + import { filter } from 'rxjs/operators'; + import { linkManager } from '@kyma-project/luigi-client'; + + @Injectable() + export class LuigiAutoNavigationService implements OnDestroy { + private subscriptions: Subscription = new Subscription(); + constructor(private router: Router) {} + public init(): void { + this.subscriptions.add( + this.router.events + .pipe(filter(ev => ev instanceof NavigationEnd)) + .subscribe((ev: NavigationEnd) => { + if (ev instanceof NavigationEnd) { + linkManager() + .fromClosestContext() + .withoutSync() + .navigate(ev.url); + } + }) + ); + } + + ngOnDestroy(): void { + this.subscriptions.unsubscribe(); + } + } +``` + +#### Result + +Other than the added service, which you can also implement as a `RouteGuard` or similar, the micro frontend is unchanged and uses `[routerLink='']` or other functionality to navigate. + +### Authenticate Luigi with Google Cloud Identity + +#### Overview + +This example shows you how to use Luigi with a Google account. + +#### Steps + +1. Register a project and generate an OAuth2 Web Client based on [Google Developers Identity - OAuth2UserAgent](https://developers.google.com/identity/protocols/OAuth2UserAgent). +2. To get your app running locally, set the Authorized JavaScript Origins URIs to `http://localhost:4200` and Authorized redirect URIs to `http://localhost:4200/luigi-core/auth/oauth2/callback.html?storageType=localStorage`. +3. Copy the Client ID which ends with `apps.googleusercontent.com`. +4. Update the LuigiConfig auth section. In this example, we have also provided a configuration for logout and getting user information: + +```javascript + { + auth: { + use: 'oAuth2ImplicitGrant', + oAuth2ImplicitGrant: { + authorizeUrl: 'https://accounts.google.com/o/oauth2/v2/auth', + oAuthData: { + response_type: 'id_token token', + client_id: 'YOUR_CLIENT_ID...apps.googleusercontent.com', + scope: 'openid https://www.googleapis.com/auth/userinfo.email profile', + } + }, + logoutFn: async (settings, authData, logoutCallback) => { + console.log('revoking token'); + await fetch(`https://accounts.google.com/o/oauth2/revoke?token=${authData.accessToken}`); + logoutCallback(); + location.href = '/logout.html'; + } + } + } +``` + +Google's `id_token` contains basic identity data like name and user ID, which allows for this data to be shown in the profile. +5. If you would also like to show the user picture, add the following code to enrich the user profile information: + +```javascript + userInfoFn: async (settings, authData) => { + const response = await fetch('https://www.googleapis.com/oauth2/v1/userinfo', { + method: 'GET', + headers: { + 'Authorization': 'Bearer ' + authData.accessToken + } + }); + const json = await response.json(); + return json; + }, +``` + + diff --git a/docs/application-setup.md b/docs/application-setup.md index 3e95c296f6..a1c804ba79 100644 --- a/docs/application-setup.md +++ b/docs/application-setup.md @@ -15,6 +15,8 @@ meta --> # Luigi Core Installation + + This document shows you how to set up a web application using the Luigi micro frontend framework. This involves a few basic steps: * Adding Luigi's `npm` packages to your project dependencies. * Giving Luigi exclusive control over your entry `index.html` file. diff --git a/docs/faq.md b/docs/faq.md index 357304cdbd..3babb38624 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -21,10 +21,16 @@ meta --> Luigi is a micro frontend framework that helps you build modularizable, extensible, scalable and consistent UIs and web applications (for administrators and business users). +You can watch this video of a Luigi use case to understand its functions better: + + ### What are micro frontends? The term "micro frontends" extends the concepts of micro services to the frontend. It's an architectural style where big frontend monoliths are decomposed into smaller and simpler chunks to be developed, tested, deployed and maintained independently and rapidly (by many distributed teams), while still appearing to the customer as a one cohesive product. +This video which explains the basics of micro frontend architecture and how it can be implemented with Luigi: + + ### Does Luigi deliver micro frontends? No, Luigi itself does not deliver any micro frontends. It is a framework that helps you develop micro frontends and connect them to web applications. diff --git a/docs/navigation-configuration.md b/docs/navigation-configuration.md index fefde6d9d0..675c8fc8c5 100644 --- a/docs/navigation-configuration.md +++ b/docs/navigation-configuration.md @@ -21,6 +21,8 @@ If you are already familiar with the basics, take a look at: * [Advanced navigation configuration](navigation-advanced.md) * [Full reference list of navigation parameters](navigation-parameters-reference.md) + + ## Navigation elements There are three main elements to Luigi: diff --git a/website/docs/.gitignore b/website/docs/.gitignore index b35e3030d0..e4f365ec3e 100644 --- a/website/docs/.gitignore +++ b/website/docs/.gitignore @@ -9,7 +9,7 @@ extendedConfiguration.bundle.js luigi-core luigi-client -public/* +public static/assets *coreStyles* navigation-generated.json diff --git a/website/docs/package-lock.json b/website/docs/package-lock.json index 8fbc130114..7df0b6f3ea 100644 --- a/website/docs/package-lock.json +++ b/website/docs/package-lock.json @@ -959,9 +959,9 @@ "integrity": "sha512-9yZatTgGE5amHVYk7vtRmHzE5uCBXRjLEhn0trsDNzECeEFGYjy30+NQWZDj9KhLEtDroqHUrGGeYTSCOlE6LA==" }, "@polka/url": { - "version": "1.0.0-next.9", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.9.tgz", - "integrity": "sha512-VZqSaulg2kVQYMulmuZcvapPwH5/y81YHANiFIKz1GNZoG/F4o1JSeLlrvXJ8tC+RPUjxdrebfT3Qn+bnMi0bA==" + "version": "1.0.0-next.11", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.11.tgz", + "integrity": "sha512-3NsZsJIA/22P3QUyrEDNA2D133H4j224twJrdipXN38dpnIOzAbUDtOwkcJ5pXmn75w7LSQDjA4tO9dm1XlqlA==" }, "@types/node": { "version": "12.11.7", @@ -6227,11 +6227,11 @@ } }, "polka": { - "version": "1.0.0-next.9", - "resolved": "https://registry.npmjs.org/polka/-/polka-1.0.0-next.9.tgz", - "integrity": "sha512-oAWH5O3CIPTzPKNx9KF9NDfy3KRyy9NtUhDEJGmMRCDT6s3CZaGDm7xafcKtm0uK6g0CBiNtoeGWpPFSLUXeaw==", + "version": "1.0.0-next.11", + "resolved": "https://registry.npmjs.org/polka/-/polka-1.0.0-next.11.tgz", + "integrity": "sha512-M/HBkS6ILksrDq7uvktCTev81OzuLwNtpxMyYdUhxLKQlMWdsu789XMotQU+p8JY8CM8vx8ML0HudyWjRus/lg==", "requires": { - "@polka/url": "^1.0.0-next.9", + "@polka/url": "^1.0.0-next.11", "trouter": "^3.1.0" } }, diff --git a/website/docs/public/index.html b/website/docs/public/index.html deleted file mode 100644 index dc012e6521..0000000000 --- a/website/docs/public/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - Documentation - Luigi - The Enterprise-Ready Micro Frontend Framework - - - - - - - - - - - - - - - - diff --git a/website/docs/src/luigi-config/extended/settings.js b/website/docs/src/luigi-config/extended/settings.js index 9ff956616f..7ffa0b686e 100644 --- a/website/docs/src/luigi-config/extended/settings.js +++ b/website/docs/src/luigi-config/extended/settings.js @@ -8,6 +8,7 @@ class Settings { responsiveNavigation = 'simpleMobileOnly'; // Options: simple | simpleMobileOnly | semiCollapsible sideNavFooterText = ' '; + customSandboxRules = ['allow-presentation'] // hideNavigation = true // backdropDisabled = true } diff --git a/website/docs/static/luigi/docsearch.min.css b/website/docs/static/luigi/docsearch.min.css deleted file mode 100644 index 9051e8de11..0000000000 --- a/website/docs/static/luigi/docsearch.min.css +++ /dev/null @@ -1,2 +0,0 @@ -.searchbox{display:inline-block;position:relative;width:200px;height:32px!important;white-space:nowrap;box-sizing:border-box;visibility:visible!important}.searchbox .algolia-autocomplete{display:block;width:100%;height:100%}.searchbox__wrapper{width:100%;height:100%;z-index:999;position:relative}.searchbox__input{display:inline-block;box-sizing:border-box;transition:box-shadow .4s ease,background .4s ease;border:0;border-radius:16px;box-shadow:inset 0 0 0 1px #ccc;background:#fff!important;padding:0 26px 0 32px;width:100%;height:100%;vertical-align:middle;white-space:normal;font-size:12px;-webkit-appearance:none;-moz-appearance:none;appearance:none}.searchbox__input::-webkit-search-cancel-button,.searchbox__input::-webkit-search-decoration,.searchbox__input::-webkit-search-results-button,.searchbox__input::-webkit-search-results-decoration{display:none}.searchbox__input:hover{box-shadow:inset 0 0 0 1px #b3b3b3}.searchbox__input:active,.searchbox__input:focus{outline:0;box-shadow:inset 0 0 0 1px #aaa;background:#fff}.searchbox__input::-webkit-input-placeholder{color:#aaa}.searchbox__input:-ms-input-placeholder{color:#aaa}.searchbox__input::-ms-input-placeholder{color:#aaa}.searchbox__input::placeholder{color:#aaa}.searchbox__submit{position:absolute;top:0;margin:0;border:0;border-radius:16px 0 0 16px;background-color:rgba(69,142,225,0);padding:0;width:32px;height:100%;vertical-align:middle;text-align:center;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;right:inherit;left:0}.searchbox__submit:before{display:inline-block;margin-right:-4px;height:100%;vertical-align:middle;content:""}.searchbox__submit:active,.searchbox__submit:hover{cursor:pointer}.searchbox__submit:focus{outline:0}.searchbox__submit svg{width:14px;height:14px;vertical-align:middle;fill:#6d7e96}.searchbox__reset{display:block;position:absolute;top:8px;right:8px;margin:0;border:0;background:none;cursor:pointer;padding:0;font-size:inherit;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;fill:rgba(0,0,0,.5)}.searchbox__reset.hide{display:none}.searchbox__reset:focus{outline:0}.searchbox__reset svg{display:block;margin:4px;width:8px;height:8px}.searchbox__input:valid~.searchbox__reset{display:block;-webkit-animation-name:sbx-reset-in;animation-name:sbx-reset-in;-webkit-animation-duration:.15s;animation-duration:.15s}@-webkit-keyframes sbx-reset-in{0%{-webkit-transform:translate3d(-20%,0,0);transform:translate3d(-20%,0,0);opacity:0}to{-webkit-transform:none;transform:none;opacity:1}}@keyframes sbx-reset-in{0%{-webkit-transform:translate3d(-20%,0,0);transform:translate3d(-20%,0,0);opacity:0}to{-webkit-transform:none;transform:none;opacity:1}}.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu{right:0!important;left:inherit!important}.algolia-autocomplete.algolia-autocomplete-right .ds-dropdown-menu:before{right:48px}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu{left:0!important;right:inherit!important}.algolia-autocomplete.algolia-autocomplete-left .ds-dropdown-menu:before{left:48px}.algolia-autocomplete .ds-dropdown-menu{top:-6px;border-radius:4px;margin:6px 0 0;padding:0;text-align:left;height:auto;position:relative;background:transparent;border:none;z-index:999;max-width:600px;min-width:500px;box-shadow:0 1px 0 0 rgba(0,0,0,.2),0 2px 3px 0 rgba(0,0,0,.1)}.algolia-autocomplete .ds-dropdown-menu:before{display:block;position:absolute;content:"";width:14px;height:14px;background:#fff;z-index:1000;top:-7px;border-top:1px solid #d9d9d9;border-right:1px solid #d9d9d9;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);border-radius:2px}.algolia-autocomplete .ds-dropdown-menu .ds-suggestions{position:relative;z-index:1000;margin-top:8px}.algolia-autocomplete .ds-dropdown-menu .ds-suggestions a:hover{text-decoration:none}.algolia-autocomplete .ds-dropdown-menu .ds-suggestion{cursor:pointer}.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion.suggestion-layout-simple,.algolia-autocomplete .ds-dropdown-menu .ds-suggestion.ds-cursor .algolia-docsearch-suggestion:not(.suggestion-layout-simple) .algolia-docsearch-suggestion--content{background-color:rgba(69,142,225,.05)}.algolia-autocomplete .ds-dropdown-menu [class^=ds-dataset-]{position:relative;border:1px solid #d9d9d9;background:#fff;border-radius:4px;overflow:auto;padding:0 8px 8px}.algolia-autocomplete .ds-dropdown-menu *{box-sizing:border-box}.algolia-autocomplete .algolia-docsearch-suggestion{display:block;position:relative;padding:0 8px;background:#fff;color:#02060c;overflow:hidden}.algolia-autocomplete .algolia-docsearch-suggestion--highlight{color:#174d8c;background:rgba(143,187,237,.1);padding:.1em .05em}.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl0 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--category-header .algolia-docsearch-suggestion--category-header-lvl1 .algolia-docsearch-suggestion--highlight,.algolia-autocomplete .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{padding:0 0 1px;background:inherit;box-shadow:inset 0 -2px 0 0 rgba(69,142,225,.8);color:inherit}.algolia-autocomplete .algolia-docsearch-suggestion--content{display:block;float:right;width:70%;position:relative;padding:5.33333px 0 5.33333px 10.66667px;cursor:pointer}.algolia-autocomplete .algolia-docsearch-suggestion--content:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;left:-1px}.algolia-autocomplete .algolia-docsearch-suggestion--category-header{position:relative;border-bottom:1px solid #ddd;display:none;margin-top:8px;padding:4px 0;font-size:1em;color:#33363d}.algolia-autocomplete .algolia-docsearch-suggestion--wrapper{width:100%;float:left;padding:8px 0 0}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column{float:left;width:30%;text-align:right;position:relative;padding:5.33333px 10.66667px;color:#a4a7ae;font-size:.9em;word-wrap:break-word}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-column:before{content:"";position:absolute;display:block;top:0;height:100%;width:1px;background:#ddd;right:0}.algolia-autocomplete .algolia-docsearch-suggestion--subcategory-inline{display:none}.algolia-autocomplete .algolia-docsearch-suggestion--title{margin-bottom:4px;color:#02060c;font-size:.9em;font-weight:700}.algolia-autocomplete .algolia-docsearch-suggestion--text{display:block;line-height:1.2em;font-size:.85em;color:#63676d}.algolia-autocomplete .algolia-docsearch-suggestion--no-results{width:100%;padding:8px 0;text-align:center;font-size:1.2em}.algolia-autocomplete .algolia-docsearch-suggestion--no-results:before{display:none}.algolia-autocomplete .algolia-docsearch-suggestion code{padding:1px 5px;font-size:90%;border:none;color:#222;background-color:#ebebeb;border-radius:3px;font-family:Menlo,Monaco,Consolas,Courier New,monospace}.algolia-autocomplete .algolia-docsearch-suggestion code .algolia-docsearch-suggestion--highlight{background:none}.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__main .algolia-docsearch-suggestion--category-header,.algolia-autocomplete .algolia-docsearch-suggestion.algolia-docsearch-suggestion__secondary{display:block}@media (min-width:768px){.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{display:block}}@media (max-width:768px){.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column{display:inline-block;width:auto;float:left;padding:0;color:#02060c;font-size:.9em;font-weight:700;text-align:left;opacity:.5}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:before{display:none}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--subcategory-column:after{content:"|"}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content{display:inline-block;width:auto;text-align:left;float:left;padding:0}.algolia-autocomplete .algolia-docsearch-suggestion .algolia-docsearch-suggestion--content:before{display:none}}.algolia-autocomplete .suggestion-layout-simple.algolia-docsearch-suggestion{border-bottom:1px solid #eee;padding:8px;margin:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content{width:100%;padding:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--content:before{display:none}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header{margin:0;padding:0;display:block;width:100%;border:none}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl0,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1{opacity:.6;font-size:.85em}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--category-header-lvl1:before{background-image:url('data:image/svg+xml;utf8,');content:"";width:10px;height:10px;display:inline-block}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--wrapper{width:100%;float:left;margin:0;padding:0}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--duplicate-content,.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--subcategory-inline{display:none!important}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title{margin:0;color:#458ee1;font-size:.9em;font-weight:400}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--title:before{content:"#";font-weight:700;color:#458ee1;display:inline-block}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text{margin:4px 0 0;display:block;line-height:1.4em;padding:5.33333px 8px;background:#f8f8f8;font-size:.85em;opacity:.8}.algolia-autocomplete .suggestion-layout-simple .algolia-docsearch-suggestion--text .algolia-docsearch-suggestion--highlight{color:#3f4145;font-weight:700;box-shadow:none}.algolia-autocomplete .algolia-docsearch-footer{width:134px;height:20px;z-index:2000;margin-top:10.66667px;float:right;font-size:0;line-height:0}.algolia-autocomplete .algolia-docsearch-footer--logo{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='168' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath d='M78.988.938h16.594a2.968 2.968 0 0 1 2.966 2.966V20.5a2.967 2.967 0 0 1-2.966 2.964H78.988a2.967 2.967 0 0 1-2.966-2.964V3.897A2.961 2.961 0 0 1 78.988.938zm41.937 17.866c-4.386.02-4.386-3.54-4.386-4.106l-.007-13.336 2.675-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-10.846-2.18c.821 0 1.43-.047 1.855-.129v-2.719a6.334 6.334 0 0 0-1.574-.199 5.7 5.7 0 0 0-.897.069 2.699 2.699 0 0 0-.814.24c-.24.116-.439.28-.582.491-.15.212-.219.335-.219.656 0 .628.219.991.616 1.23s.938.362 1.615.362zm-.233-9.7c.883 0 1.629.109 2.231.328.602.218 1.088.525 1.444.915.363.396.609.922.76 1.483.157.56.232 1.175.232 1.85v6.874a32.5 32.5 0 0 1-1.868.314c-.834.123-1.772.185-2.813.185-.69 0-1.327-.069-1.895-.198a4.001 4.001 0 0 1-1.471-.636 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.803 0-.656.13-1.073.384-1.525a3.24 3.24 0 0 1 1.047-1.106c.445-.287.95-.492 1.532-.615a8.8 8.8 0 0 1 1.82-.185 8.404 8.404 0 0 1 1.972.24v-.438c0-.307-.035-.6-.11-.874a1.88 1.88 0 0 0-.384-.73 1.784 1.784 0 0 0-.724-.493 3.164 3.164 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.735 7.735 0 0 0-1.26.307l-.321-2.192c.335-.117.834-.233 1.478-.349a10.98 10.98 0 0 1 2.073-.178zm52.842 9.626c.822 0 1.43-.048 1.854-.13V13.7a6.347 6.347 0 0 0-1.574-.199c-.294 0-.595.021-.896.069a2.7 2.7 0 0 0-.814.24 1.46 1.46 0 0 0-.582.491c-.15.212-.218.335-.218.656 0 .628.218.991.615 1.23.404.245.938.362 1.615.362zm-.226-9.694c.883 0 1.629.108 2.231.327.602.219 1.088.526 1.444.915.355.39.609.923.759 1.483a6.8 6.8 0 0 1 .233 1.852v6.873c-.41.088-1.034.19-1.868.314-.834.123-1.772.184-2.813.184-.69 0-1.327-.068-1.895-.198a4.001 4.001 0 0 1-1.471-.635 3.085 3.085 0 0 1-.951-1.134c-.226-.465-.343-1.12-.343-1.804 0-.656.13-1.073.384-1.524.26-.45.608-.82 1.047-1.107.445-.286.95-.491 1.532-.614a8.803 8.803 0 0 1 2.751-.13c.329.034.671.096 1.04.185v-.437a3.3 3.3 0 0 0-.109-.875 1.873 1.873 0 0 0-.384-.731 1.784 1.784 0 0 0-.724-.492 3.165 3.165 0 0 0-1.143-.205c-.616 0-1.177.075-1.69.164a7.75 7.75 0 0 0-1.26.307l-.321-2.193c.335-.116.834-.232 1.478-.348a11.633 11.633 0 0 1 2.073-.177zm-8.034-1.271a1.626 1.626 0 0 1-1.628-1.62c0-.895.725-1.62 1.628-1.62.904 0 1.63.725 1.63 1.62 0 .895-.733 1.62-1.63 1.62zm1.348 13.22h-2.689V7.27l2.69-.423v11.956zm-4.714 0c-4.386.02-4.386-3.54-4.386-4.107l-.008-13.336 2.676-.424v13.254c0 .322 0 2.358 1.718 2.364v2.248zm-8.698-5.903c0-1.156-.253-2.119-.746-2.788-.493-.677-1.183-1.01-2.067-1.01-.882 0-1.574.333-2.065 1.01-.493.676-.733 1.632-.733 2.788 0 1.168.246 1.953.74 2.63.492.683 1.183 1.018 2.066 1.018.882 0 1.574-.342 2.067-1.019.492-.683.738-1.46.738-2.63zm2.737-.007c0 .902-.13 1.584-.397 2.33a5.52 5.52 0 0 1-1.128 1.906 4.986 4.986 0 0 1-1.752 1.223c-.685.286-1.739.45-2.265.45-.528-.006-1.574-.157-2.252-.45a5.096 5.096 0 0 1-1.744-1.223c-.487-.527-.863-1.162-1.137-1.906a6.345 6.345 0 0 1-.41-2.33c0-.902.123-1.77.397-2.508a5.554 5.554 0 0 1 1.15-1.892 5.133 5.133 0 0 1 1.75-1.216c.679-.287 1.425-.423 2.232-.423.808 0 1.553.142 2.237.423a4.88 4.88 0 0 1 1.753 1.216 5.644 5.644 0 0 1 1.135 1.892c.287.738.431 1.606.431 2.508zm-20.138 0c0 1.12.246 2.363.738 2.882.493.52 1.13.78 1.91.78.424 0 .828-.062 1.204-.178.377-.116.677-.253.917-.417V9.33a10.476 10.476 0 0 0-1.766-.226c-.971-.028-1.71.37-2.23 1.004-.513.636-.773 1.75-.773 2.788zm7.438 5.274c0 1.824-.466 3.156-1.404 4.004-.936.846-2.367 1.27-4.296 1.27-.705 0-2.17-.137-3.34-.396l.431-2.118c.98.205 2.272.26 2.95.26 1.074 0 1.84-.219 2.299-.656.459-.437.684-1.086.684-1.948v-.437a8.07 8.07 0 0 1-1.047.397c-.43.13-.93.198-1.492.198-.739 0-1.41-.116-2.018-.349a4.206 4.206 0 0 1-1.567-1.025c-.431-.45-.774-1.017-1.013-1.694-.24-.677-.363-1.885-.363-2.773 0-.834.13-1.88.384-2.577.26-.696.629-1.298 1.129-1.796.493-.498 1.095-.881 1.8-1.162a6.605 6.605 0 0 1 2.428-.457c.87 0 1.67.109 2.45.24.78.129 1.444.265 1.985.415V18.17z' fill='%235468FF'/%3E%3Cpath d='M6.972 6.677v1.627c-.712-.446-1.52-.67-2.425-.67-.585 0-1.045.13-1.38.391a1.24 1.24 0 0 0-.502 1.03c0 .425.164.765.494 1.02.33.256.835.532 1.516.83.447.192.795.356 1.045.495.25.138.537.332.862.582.324.25.563.548.718.894.154.345.23.741.23 1.188 0 .947-.334 1.691-1.004 2.234-.67.542-1.537.814-2.601.814-1.18 0-2.16-.229-2.936-.686v-1.708c.84.628 1.814.942 2.92.942.585 0 1.048-.136 1.388-.407.34-.271.51-.646.51-1.125 0-.287-.1-.55-.302-.79-.203-.24-.42-.42-.655-.542-.234-.123-.585-.29-1.053-.503a61.27 61.27 0 0 1-.582-.271 13.67 13.67 0 0 1-.55-.287 4.275 4.275 0 0 1-.567-.351 6.92 6.92 0 0 1-.455-.4c-.18-.17-.31-.34-.39-.51-.08-.17-.155-.37-.224-.598a2.553 2.553 0 0 1-.104-.742c0-.915.333-1.638.998-2.17.664-.532 1.523-.798 2.576-.798.968 0 1.793.17 2.473.51zm7.468 5.696v-.287c-.022-.607-.187-1.088-.495-1.444-.309-.357-.75-.535-1.324-.535-.532 0-.99.194-1.373.583-.382.388-.622.949-.717 1.683h3.909zm1.005 2.792v1.404c-.596.34-1.383.51-2.362.51-1.255 0-2.255-.377-3-1.132-.744-.755-1.116-1.744-1.116-2.968 0-1.297.34-2.316 1.021-3.055.68-.74 1.548-1.11 2.6-1.11 1.033 0 1.852.323 2.458.966.606.644.91 1.572.91 2.784 0 .33-.033.676-.096 1.038h-5.314c.107.702.405 1.239.894 1.611.49.372 1.106.558 1.85.558.862 0 1.58-.202 2.155-.606zm6.605-1.77h-1.212c-.596 0-1.045.116-1.349.35-.303.234-.454.532-.454.894 0 .372.117.664.35.877.235.213.575.32 1.022.32.51 0 .912-.142 1.204-.424.293-.281.44-.651.44-1.108v-.91zm-4.068-2.554V9.325c.627-.361 1.457-.542 2.489-.542 2.116 0 3.175 1.026 3.175 3.08V17h-1.548v-.957c-.415.68-1.143 1.02-2.186 1.02-.766 0-1.38-.22-1.843-.661-.462-.442-.694-1.003-.694-1.684 0-.776.293-1.38.878-1.81.585-.431 1.404-.647 2.457-.647h1.34V11.8c0-.554-.133-.971-.399-1.253-.266-.282-.707-.423-1.324-.423a4.07 4.07 0 0 0-2.345.718zm9.333-1.93v1.42c.394-1 1.101-1.5 2.123-1.5.148 0 .313.016.494.048v1.531a1.885 1.885 0 0 0-.75-.143c-.542 0-.989.24-1.34.718-.351.479-.527 1.048-.527 1.707V17h-1.563V8.91h1.563zm5.01 4.084c.022.82.272 1.492.75 2.019.479.526 1.15.79 2.01.79.639 0 1.235-.176 1.788-.527v1.404c-.521.319-1.186.479-1.995.479-1.265 0-2.276-.4-3.031-1.197-.755-.798-1.133-1.792-1.133-2.984 0-1.16.38-2.151 1.14-2.975.761-.825 1.79-1.237 3.088-1.237.702 0 1.346.149 1.93.447v1.436a3.242 3.242 0 0 0-1.77-.495c-.84 0-1.513.266-2.019.798-.505.532-.758 1.213-.758 2.042zM40.24 5.72v4.579c.458-1 1.293-1.5 2.505-1.5.787 0 1.42.245 1.899.734.479.49.718 1.17.718 2.042V17h-1.564v-5.106c0-.553-.14-.98-.422-1.284-.282-.303-.652-.455-1.11-.455-.531 0-1.002.202-1.411.606-.41.405-.615 1.022-.615 1.851V17h-1.563V5.72h1.563zm14.966 10.02c.596 0 1.096-.253 1.5-.758.404-.506.606-1.157.606-1.955 0-.915-.202-1.62-.606-2.114-.404-.495-.92-.742-1.548-.742-.553 0-1.05.224-1.491.67-.442.447-.662 1.133-.662 2.058 0 .958.212 1.67.638 2.138.425.469.946.703 1.563.703zM53.004 5.72v4.42c.574-.894 1.388-1.341 2.44-1.341 1.022 0 1.857.383 2.506 1.149.649.766.973 1.781.973 3.047 0 1.138-.309 2.109-.925 2.912-.617.803-1.463 1.205-2.537 1.205-1.075 0-1.894-.447-2.457-1.34V17h-1.58V5.72h1.58zm9.908 11.104l-3.223-7.913h1.739l1.005 2.632 1.26 3.415c.096-.32.48-1.458 1.15-3.415l.909-2.632h1.66l-2.92 7.866c-.777 2.074-1.963 3.11-3.559 3.11a2.92 2.92 0 0 1-.734-.079v-1.34c.17.042.351.064.543.064 1.032 0 1.755-.57 2.17-1.708z' fill='%235D6494'/%3E%3Cpath d='M89.632 5.967v-.772a.978.978 0 0 0-.978-.977h-2.28a.978.978 0 0 0-.978.977v.793c0 .088.082.15.171.13a7.127 7.127 0 0 1 1.984-.28c.65 0 1.295.088 1.917.259.082.02.164-.04.164-.13m-6.248 1.01l-.39-.389a.977.977 0 0 0-1.382 0l-.465.465a.973.973 0 0 0 0 1.38l.383.383c.062.061.15.047.205-.014.226-.307.472-.601.746-.874.281-.28.568-.526.883-.751.068-.042.075-.137.02-.2m4.16 2.453v3.341c0 .096.104.165.192.117l2.97-1.537c.068-.034.089-.117.055-.184a3.695 3.695 0 0 0-3.08-1.866c-.068 0-.136.054-.136.13m0 8.048a4.489 4.489 0 0 1-4.49-4.482 4.488 4.488 0 0 1 4.49-4.482 4.488 4.488 0 0 1 4.489 4.482 4.484 4.484 0 0 1-4.49 4.482m0-10.85a6.363 6.363 0 1 0 0 12.729 6.37 6.37 0 0 0 6.372-6.368 6.358 6.358 0 0 0-6.371-6.36' fill='%23FFF'/%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;background-position:50%;background-size:100%;overflow:hidden;text-indent:-9000px;padding:0!important;width:100%;height:100%;display:block} -/*# sourceMappingURL=docsearch.min.css.map */ \ No newline at end of file diff --git a/website/docs/static/luigi/docsearch.min.js b/website/docs/static/luigi/docsearch.min.js deleted file mode 100644 index efb45b8f6f..0000000000 --- a/website/docs/static/luigi/docsearch.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! docsearch 2.6.3 | © Algolia | github.com/algolia/docsearch */ -(function webpackUniversalModuleDefinition(root,factory){if(typeof exports==="object"&&typeof module==="object")module.exports=factory();else if(typeof define==="function"&&define.amd)define([],factory);else if(typeof exports==="object")exports["docsearch"]=factory();else root["docsearch"]=factory()})(typeof self!=="undefined"?self:this,function(){return function(modules){var installedModules={};function __webpack_require__(moduleId){if(installedModules[moduleId]){return installedModules[moduleId].exports}var module=installedModules[moduleId]={i:moduleId,l:false,exports:{}};modules[moduleId].call(module.exports,module,module.exports,__webpack_require__);module.l=true;return module.exports}__webpack_require__.m=modules;__webpack_require__.c=installedModules;__webpack_require__.d=function(exports,name,getter){if(!__webpack_require__.o(exports,name)){Object.defineProperty(exports,name,{configurable:false,enumerable:true,get:getter})}};__webpack_require__.n=function(module){var getter=module&&module.__esModule?function getDefault(){return module["default"]}:function getModuleExports(){return module};__webpack_require__.d(getter,"a",getter);return getter};__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)};__webpack_require__.p="";return __webpack_require__(__webpack_require__.s=22)}([function(module,exports,__webpack_require__){"use strict";var DOM=__webpack_require__(1);function escapeRegExp(str){return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")}module.exports={isArray:null,isFunction:null,isObject:null,bind:null,each:null,map:null,mixin:null,isMsie:function(agentString){if(agentString===undefined){agentString=navigator.userAgent}if(/(msie|trident)/i.test(agentString)){var match=agentString.match(/(msie |rv:)(\d+(.\d+)?)/i);if(match){return match[2]}}return false},escapeRegExChars:function(str){return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isNumber:function(obj){return typeof obj==="number"},toStr:function toStr(s){return s===undefined||s===null?"":s+""},cloneDeep:function cloneDeep(obj){var clone=this.mixin({},obj);var self=this;this.each(clone,function(value,key){if(value){if(self.isArray(value)){clone[key]=[].concat(value)}else if(self.isObject(value)){clone[key]=self.cloneDeep(value)}}});return clone},error:function(msg){throw new Error(msg)},every:function(obj,test){var result=true;if(!obj){return result}this.each(obj,function(val,key){if(result){result=test.call(null,val,key,obj)&&result}});return!!result},any:function(obj,test){var found=false;if(!obj){return found}this.each(obj,function(val,key){if(test.call(null,val,key,obj)){found=true;return false}});return found},getUniqueId:function(){var counter=0;return function(){return counter++}}(),templatify:function templatify(obj){if(this.isFunction(obj)){return obj}var $template=DOM.element(obj);if($template.prop("tagName")==="SCRIPT"){return function template(){return $template.text()}}return function template(){return String(obj)}},defer:function(fn){setTimeout(fn,0)},noop:function(){},formatPrefix:function(prefix,noPrefix){return noPrefix?"":prefix+"-"},className:function(prefix,clazz,skipDot){return(skipDot?"":".")+prefix+clazz},escapeHighlightedString:function(str,highlightPreTag,highlightPostTag){highlightPreTag=highlightPreTag||"";var pre=document.createElement("div");pre.appendChild(document.createTextNode(highlightPreTag));highlightPostTag=highlightPostTag||"";var post=document.createElement("div");post.appendChild(document.createTextNode(highlightPostTag));var div=document.createElement("div");div.appendChild(document.createTextNode(str));return div.innerHTML.replace(RegExp(escapeRegExp(pre.innerHTML),"g"),highlightPreTag).replace(RegExp(escapeRegExp(post.innerHTML),"g"),highlightPostTag)}}},function(module,exports,__webpack_require__){"use strict";module.exports={element:null}},function(module,exports){var hasOwn=Object.prototype.hasOwnProperty;var toString=Object.prototype.toString;module.exports=function forEach(obj,fn,ctx){if(toString.call(fn)!=="[object Function]"){throw new TypeError("iterator must be a function")}var l=obj.length;if(l===+l){for(var i=0;i was loaded but did not call our provided callback"),JSONPScriptError:createCustomError("JSONPScriptError"," + diff --git a/website/fiddle/package.json b/website/fiddle/package.json index 152d08efd8..e262a5de33 100644 --- a/website/fiddle/package.json +++ b/website/fiddle/package.json @@ -25,4 +25,4 @@ "ace-builds": "^1.4.5", "fiori-fundamentals": "1.7.1" } -} +} \ No newline at end of file