@nextcloud/vue
Version:
Nextcloud vue components
1 lines • 21.7 kB
Source Map (JSON)
{"version":3,"file":"NcAppContent.mjs","sources":["../../src/components/NcAppContent/NcAppContentDetailsToggle.vue","../../src/components/NcAppContent/NcAppContent.vue"],"sourcesContent":["<!--\n - SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n\n<script setup lang=\"ts\">\nimport { mdiArrowRight } from '@mdi/js'\nimport { emit } from '@nextcloud/event-bus'\nimport { onBeforeUnmount, onMounted, watch } from 'vue'\nimport { useIsMobile } from '../../composables/useIsMobile/index.js'\nimport { t } from '../../l10n.ts'\nimport NcButton from '../NcButton/index.ts'\nimport NcIconSvgWrapper from '../NcIconSvgWrapper/index.ts'\n\nconst isMobile = useIsMobile()\nwatch(isMobile, toggleAppNavigationButton)\n\nonMounted(() => {\n\ttoggleAppNavigationButton(isMobile.value)\n})\n\nonBeforeUnmount(() => {\n\tif (isMobile.value) {\n\t\ttoggleAppNavigationButton(false)\n\t}\n})\n\n/**\n * Toggle the app navigation button and hide it if needed.\n *\n * @param hide - if true the navigation toggle is visually hidden\n */\nfunction toggleAppNavigationButton(hide: boolean = true) {\n\tconst appNavigationToggle = document.querySelector<HTMLElement>('.app-navigation .app-navigation-toggle')\n\tif (appNavigationToggle) {\n\t\tappNavigationToggle.style.display = hide ? 'none' : ''\n\n\t\t// If we hide the NavigationToggle, we need to make sure the Navigation is also closed\n\t\tif (hide === true) {\n\t\t\temit('toggle-navigation', { open: false })\n\t\t}\n\t}\n}\n</script>\n\n<template>\n\t<NcButton\n\t\t:aria-label=\"t('Go back to the list')\"\n\t\tclass=\"app-details-toggle\"\n\t\t:class=\"{ 'app-details-toggle--mobile': isMobile }\"\n\t\t:title=\"t('Go back to the list')\"\n\t\tvariant=\"tertiary\">\n\t\t<template #icon>\n\t\t\t<NcIconSvgWrapper directional :path=\"mdiArrowRight\" />\n\t\t</template>\n\t</NcButton>\n</template>\n\n<style lang=\"scss\" scoped>\n.app-details-toggle {\n\tposition: sticky;\n\twidth: var(--default-clickable-area);\n\theight: var(--default-clickable-area);\n\tpadding: $icon-margin;\n\tcursor: pointer;\n\topacity: .6;\n\ttransform: rotate(180deg);\n\tbackground-color: var(--color-main-background);\n\tz-index: 2000;\n\n\ttop: var(--app-navigation-padding);\n\t// Navigation Toggle button width + 2 paddings around\n\tinset-inline-start: calc(var(--default-clickable-area) + var(--app-navigation-padding) * 2);\n\t&--mobile {\n\t\t// There is no NavigationToggle button\n\t\tinset-inline-start: var(--app-navigation-padding);\n\t}\n\n\t&:active,\n\t&:hover,\n\t&:focus {\n\t\topacity: 1;\n\t}\n}\n\n</style>\n","<!--\n - SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors\n - SPDX-License-Identifier: AGPL-3.0-or-later\n-->\n\n<script lang=\"ts\">\nimport { getBuilder } from '@nextcloud/browser-storage'\nimport { getCapabilities } from '@nextcloud/capabilities'\n\n/**\n * Content layout used when a list is provided together with the main content.\n */\nexport type AppContentLayout = 'no-split' | 'vertical-split' | 'horizontal-split'\n\n/**\n * Payload of the `resized` event of the splitpanes component.\n */\ninterface SplitpanesResizedEvent {\n\tpanes: { min: number, max: number, size: number }[]\n}\n\nconst browserStorage = getBuilder('nextcloud').persist().build()\nconst instanceName = (getCapabilities() as { theming?: { name?: string } }).theming?.name ?? 'Nextcloud'\n</script>\n\n<script setup lang=\"ts\">\nimport type { Slot } from 'vue'\n\nimport { emit as emitBusEvent } from '@nextcloud/event-bus'\nimport { useCurrentElement, useSwipe } from '@vueuse/core'\nimport { Pane, Splitpanes } from 'splitpanes'\nimport { computed, onMounted, ref, watch } from 'vue'\nimport NcAppContentDetailsToggle from './NcAppContentDetailsToggle.vue'\nimport { useIsMobile } from '../../composables/useIsMobile/index.ts'\nimport { useAppName, useLocalizedAppName } from '../../utils/appName.ts'\nimport { logger } from '../../utils/logger.ts'\nimport { isRtl } from '../../utils/rtl.ts'\n\nimport 'splitpanes/dist/splitpanes.css'\n\n/**\n * When in mobile view, only the list or the details are shown.\n *\n * If you provide a list, you need to provide a variable\n * that will be set to true by the user when an element of\n * the list gets selected. The details will then show a back\n * arrow to return to the list that will update this model to false.\n */\nconst showDetails = defineModel<boolean>('showDetails', { default: true })\n\nconst {\n\tdisableSwipe = false,\n\tlayout = 'vertical-split',\n\tlistMaxWidth = 40,\n\tlistMinWidth = 15,\n\tlistSize = 20,\n\tpageHeading = undefined,\n\tpageTitle = undefined,\n\tpaneConfigKey = '',\n} = defineProps<{\n\t/**\n\t * Allows to disable the control by swipe of the app navigation open state.\n\t */\n\tdisableSwipe?: boolean\n\n\t/**\n\t * Allows you to set the default width of the resizable list in % on vertical-split\n\t * or respectively the default height on horizontal-split.\n\t *\n\t * Must be between `listMinWidth` and `listMaxWidth`.\n\t *\n\t * @default 20\n\t */\n\tlistSize?: number\n\n\t/**\n\t * Allows you to set the minimum width of the list column in % on vertical-split\n\t * or respectively the minimum height on horizontal-split.\n\t *\n\t * @default 15\n\t */\n\tlistMinWidth?: number\n\n\t/**\n\t * Allows you to set the maximum width of the list column in % on vertical-split\n\t * or respectively the maximum height on horizontal-split.\n\t *\n\t * @default 40\n\t */\n\tlistMaxWidth?: number\n\n\t/**\n\t * Specify the config key for the pane config sizes\n\t * Default is the global var appName if you use the webpack-vue-config\n\t */\n\tpaneConfigKey?: string\n\n\t/**\n\t * Content layout used when there is a list together with content:\n\t * - `vertical-split` - a 2-column layout with list and default content separated vertically\n\t * - `no-split` - a single column layout; List is shown when `showDetails` is `false`, otherwise the default slot content is shown with a back button to return to the list.\n\t * - 'horizontal-split' - a 2-column layout with list and default content separated horizontally\n\t * On mobile screen `no-split` layout is forced.\n\t *\n\t * @default 'vertical-split'\n\t */\n\tlayout?: AppContentLayout\n\n\t/**\n\t * Specify the `<h1>` page heading\n\t */\n\tpageHeading?: string | null\n\n\t/**\n\t * Allow setting the page's `<title>`\n\t *\n\t * If a page heading is set it defaults to `{pageHeading} - {appName} - {instanceName}` e.g. `Favorites - Files - MyPersonalCloud`.\n\t * When the page heading and the app name is the same only one is used, e.g. `Files - Files - MyPersonalCloud` is shown as `Files - MyPersonalCloud`.\n\t * When setting the prop then the following format will be used: `{pageTitle} - {instanceName}`\n\t */\n\tpageTitle?: string | null\n}>()\n\nconst emit = defineEmits<{\n\t/**\n\t * Emitted when the list pane is resized by the user\n\t */\n\tresizeList: [payload: { size: number }]\n\n\t/**\n\t * Emitted when the user clicked the back arrow from the details view\n\t *\n\t * @param showDetails - The new details state\n\t */\n\t'update:showDetails': [showDetails: boolean]\n}>()\n\ndefineSlots<{\n\t/**\n\t * Provide the main content to the app content\n\t */\n\tdefault?: Slot\n\n\t/**\n\t * Provide a list to the app content\n\t */\n\tlist?: Slot\n}>()\n\nconst appName = useAppName()\nconst localizedAppName = useLocalizedAppName()\nconst isMobile = useIsMobile()\n\nconst element = useCurrentElement<HTMLElement>()\nconst swipe = useSwipe(\n\t// Swiping can be disabled by not providing a target to listen on\n\t() => disableSwipe ? undefined : element.value,\n\t{\n\t\tonSwipeEnd(_event, direction) {\n\t\t\tconst minSwipeX = 70\n\t\t\tconst touchZone = 300\n\t\t\tif (Math.abs(swipe.lengthX.value) > minSwipeX) {\n\t\t\t\tif (swipe.coordsStart.x < (touchZone / 2) && direction === 'right') {\n\t\t\t\t\temitBusEvent('toggle-navigation', {\n\t\t\t\t\t\topen: true,\n\t\t\t\t\t})\n\t\t\t\t} else if (swipe.coordsStart.x < touchZone * 1.5 && direction === 'left') {\n\t\t\t\t\temitBusEvent('toggle-navigation', {\n\t\t\t\t\t\topen: false,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t},\n)\n\n/**\n * Size of the list pane as restored from the browser storage\n */\nconst listPaneSize = ref<number>()\n\nconst paneConfigID = computed(() => {\n\t// If provided, let's use it\n\tif (paneConfigKey !== '') {\n\t\treturn `pane-list-size-${paneConfigKey}`\n\t}\n\n\treturn `pane-list-size-${appName}`\n})\n\nconst paneDefaults = computed(() => ({\n\tlist: {\n\t\tsize: listSize,\n\t\tmin: listMinWidth,\n\t\tmax: listMaxWidth,\n\t},\n\n\t// set the inverse values of the details column\n\t// based on the provided (or default) values of the list column\n\tdetails: {\n\t\tsize: 100 - listSize,\n\t\tmin: 100 - listMaxWidth,\n\t\tmax: 100 - listMinWidth,\n\t},\n}))\n\nconst detailsPaneSize = computed(() => {\n\tif (listPaneSize.value) {\n\t\treturn 100 - listPaneSize.value\n\t}\n\treturn paneDefaults.value.details.size\n})\n\nconst realPageTitle = computed<string | null>(() => {\n\tconst entries = new Set<string>()\n\tif (pageTitle) {\n\t\t// when page title is set we only use that\n\t\t// we still split to remove duplicated instanceName\n\t\tfor (const part of pageTitle.split(' - ')) {\n\t\t\tentries.add(part)\n\t\t}\n\t} else if (pageHeading) {\n\t\t// when the page heading is provided but not the title\n\t\t// then we split to remove duplicates\n\t\t// but also add the localized app name\n\t\tfor (const part of pageHeading.split(' - ')) {\n\t\t\tentries.add(part)\n\t\t}\n\n\t\tif (entries.size > 0) {\n\t\t\tentries.add(localizedAppName)\n\t\t}\n\t} else {\n\t\treturn null\n\t}\n\n\tentries.add(instanceName)\n\treturn [...entries.values()].join(' - ')\n})\n\nwatch(realPageTitle, () => {\n\tif (realPageTitle.value !== null) {\n\t\tdocument.title = realPageTitle.value\n\t}\n}, { immediate: true })\n\nwatch(() => paneConfigKey, restorePaneConfig, { immediate: true })\n\nonMounted(restorePaneConfig)\n\n/**\n * Handle the resize of the list pane by the user.\n *\n * @param event - The `resized` event of the splitpanes component\n */\nfunction handlePaneResize(event: SplitpanesResizedEvent): void {\n\tconst size = Math.trunc(event.panes[0].size)\n\tbrowserStorage.setItem(paneConfigID.value, JSON.stringify(size))\n\tlistPaneSize.value = size\n\temit('resizeList', { size })\n\tlogger.debug('[NcAppContent] pane config', { listPaneSize: size })\n}\n\n/**\n * Restore the size of the list pane from the browser storage.\n *\n * browserStorage is not reactive, we need to update this manually.\n */\nfunction restorePaneConfig(): void {\n\tconst size = parseInt(browserStorage.getItem(paneConfigID.value) ?? '', 10)\n\tif (!isNaN(size) && size !== listPaneSize.value) {\n\t\tlogger.debug('[NcAppContent] pane config', { listPaneSize: size })\n\t\tlistPaneSize.value = size\n\t}\n}\n</script>\n\n<template>\n\t<main id=\"app-content-vue\" class=\"app-content no-snapper\" :class=\"{ 'app-content--has-list': !!$slots.list }\">\n\t\t<h1 v-if=\"pageHeading\" class=\"hidden-visually\">\n\t\t\t{{ pageHeading }}\n\t\t</h1>\n\n\t\t<template v-if=\"!!$slots.list\">\n\t\t\t<!-- Mobile view does not allow resizeable panes -->\n\t\t\t<div\n\t\t\t\tv-if=\"isMobile || layout === 'no-split'\"\n\t\t\t\tclass=\"app-content-wrapper app-content-wrapper--no-split\"\n\t\t\t\t:class=\"{\n\t\t\t\t\t'app-content-wrapper--show-details': showDetails,\n\t\t\t\t\t'app-content-wrapper--show-list': !showDetails,\n\t\t\t\t\t'app-content-wrapper--mobile': isMobile,\n\t\t\t\t}\">\n\t\t\t\t<NcAppContentDetailsToggle v-if=\"showDetails\" @click.stop.prevent=\"showDetails = false\" />\n\n\t\t\t\t<div v-show=\"!showDetails\" class=\"app-content-wrapper__list\">\n\t\t\t\t\t<slot name=\"list\" />\n\t\t\t\t</div>\n\t\t\t\t<slot v-if=\"showDetails\" />\n\t\t\t</div>\n\t\t\t<div v-else-if=\"layout === 'vertical-split' || layout === 'horizontal-split'\" class=\"app-content-wrapper\">\n\t\t\t\t<Splitpanes\n\t\t\t\t\t:horizontal=\"layout === 'horizontal-split'\"\n\t\t\t\t\tclass=\"default-theme\"\n\t\t\t\t\t:class=\"{\n\t\t\t\t\t\t'splitpanes--horizontal': layout === 'horizontal-split',\n\t\t\t\t\t\t'splitpanes--vertical': layout === 'vertical-split',\n\t\t\t\t\t}\"\n\t\t\t\t\t:rtl=\"isRtl\"\n\t\t\t\t\t@resized=\"handlePaneResize\">\n\t\t\t\t\t<Pane\n\t\t\t\t\t\tclass=\"splitpanes__pane-list\"\n\t\t\t\t\t\t:size=\"listPaneSize || paneDefaults.list.size\"\n\t\t\t\t\t\t:minSize=\"paneDefaults.list.min\"\n\t\t\t\t\t\t:maxSize=\"paneDefaults.list.max\">\n\t\t\t\t\t\t<slot name=\"list\" />\n\t\t\t\t\t</Pane>\n\n\t\t\t\t\t<Pane\n\t\t\t\t\t\tclass=\"splitpanes__pane-details\"\n\t\t\t\t\t\t:size=\"detailsPaneSize\"\n\t\t\t\t\t\t:minSize=\"paneDefaults.details.min\"\n\t\t\t\t\t\t:maxSize=\"paneDefaults.details.max\">\n\t\t\t\t\t\t<slot />\n\t\t\t\t\t</Pane>\n\t\t\t\t</Splitpanes>\n\t\t\t</div>\n\t\t</template>\n\t\t<slot v-if=\"!$slots.list\" />\n\t</main>\n</template>\n\n<style lang=\"scss\" scoped>\n\n.app-content {\n\tposition: initial;\n\tz-index: 1000;\n\tflex-basis: 100vw;\n\theight: 100%;\n\t// Overriding server styles TODO: cleanup!\n\tmargin: 0 !important;\n\tbackground-color: var(--color-main-background);\n\tmin-width: 0;\n\n\t&:not(.app-content--has-list) {\n\t\toverflow: auto;\n\t}\n}\n\n.app-content-wrapper {\n\tposition: relative;\n\twidth: 100%;\n\theight: 100%;\n}\n\n// Mobile list/details handling\n.app-content-wrapper--no-split {\n\t&.app-content-wrapper--show-list :deep() {\n\t\t.app-content-list {\n\t\t\tdisplay: flex;\n\t\t}\n\t\t.app-content-details {\n\t\t\tdisplay: none;\n\t\t}\n\t}\n\t&.app-content-wrapper--show-details :deep() {\n\t\t.app-content-list {\n\t\t\tdisplay: none;\n\t\t}\n\t\t.app-content-details {\n\t\t\tdisplay: block;\n\t\t}\n\t}\n}\n\n:deep(.splitpanes.default-theme) {\n\t.app-content-list {\n\t\tmax-width: none;\n\t\t/* Thin scrollbar is hard to catch on resizable columns */\n\t\tscrollbar-width: auto;\n\t}\n\n\t.splitpanes__pane {\n\t\tbackground-color: transparent;\n\t\ttransition: none;\n\n\t\t&-list {\n\t\t\tmin-width: 300px;\n\t\t\tposition: sticky;\n\n\t\t\t@media only screen and (width < $breakpoint-mobile) {\n\t\t\t\tdisplay: none;\n\t\t\t}\n\t\t}\n\n\t\t&-details {\n\t\t\toverflow-y: auto;\n\n\t\t\t@media only screen and (width < $breakpoint-mobile) {\n\t\t\t\tmin-width: 100%;\n\t\t\t}\n\t\t}\n\t}\n\n\t.splitpanes__splitter {\n\t\tbackground-color: var(--color-main-background);\n\t\t&::before, &::after {\n\t\t\tbackground-color: var(--color-border);\n\t\t}\n\t}\n\n\t&.splitpanes--vertical .splitpanes__splitter {\n\t\tborder-inline-start: 1px solid var(--color-border);\n\t}\n\n\t&.splitpanes--horizontal .splitpanes__splitter {\n\t\tborder-top: 1px solid var(--color-border);\n\t}\n}\n\n.app-content-wrapper--show-list {\n\t:deep(.app-content-list) {\n\t\tmax-width: none;\n\t}\n}\n\n.app-content-wrapper__list {\n\theight: 100%;\n}\n</style>\n\n<docs>\n### General description\n\nThis components provides a wrapper around the main app's content.\n\nSingle-column layouts can just use the default slot. A resizable column\ncan be added by providing content to the named slot `list`.\n\n### CSS variables\nIn the css section some css variables are declared and will be available for\nall the children of the NcAppContent component\n\n### Examples\n\n#### Usage: Single-column content\n```vue\n<template>\n\t<NcAppContent>\n\t\t<h2>Single-column main content</h2>\n\t</NcAppContent>\n</template>\n```\n\n#### Usage: Two resizable columns\n```vue\n<template>\n\t<NcAppContent>\n\t\t<template #list>\n\t\t\t<div>Resizable list content</div>\n\t\t</template>\n\n\t\t<div>Main content</div>\n\t</NcAppContent>\n</template>\n```\n\n#### Overriding Defaults\nThe default, min and max sizes (in percent) of the resizable list column can be overridden.\nThe list size must be between the min and the max width value.\n\n```\n<NcAppContent\n\t:list-size=\"35\"\n\t:list-min-width=\"20\"\n\t:list-max-width=\"45\"\n>...</NcAppContent>\n```\n\n#### Usage: Custom document title\nFor accessibility reasons every document should have a `h1` heading,\nthis is visually hidden, but required for a semantically correct document.\nYou can use your app name or current view for the heading.\n\nAdditionally you can set a custom document title, e.g. to show the current status.\n\n```vue\n<template>\n\t<NcAppContent :pageHeading=\"heading ? 'Heading' : undefined\" :pageTitle=\"title ? 'Title' : undefined\" >\n\t\t<NcCheckboxRadioSwitch type=\"switch\" :checked.sync=\"title\">\n\t\t\tToggle title\n\t\t</NcCheckboxRadioSwitch>\n\t\t<NcCheckboxRadioSwitch type=\"switch\" :checked.sync=\"heading\">\n\t\t\tToggle Heading\n\t\t</NcCheckboxRadioSwitch>\n\t\t<NcButton @click=\"reset\">Reset</NcButton>\n\t</NcAppContent>\n</template>\n\n<script>\nexport default {\n\tdata() {\n\t\treturn {\n\t\t\theading: false,\n\t\t\ttitle: false,\n\t\t}\n\t},\n\tmethods: {\n\t\treset() {\n\t\t\tthis.heading = false\n\t\t\tthis.title = false\n\t\t\tdocument.title = ''\n\t\t},\n\t},\n}\n</script>\n```\n</docs>\n"],"names":["_createBlock","_unref","_normalizeClass","_createVNode","_useModel","emit","emitBusEvent","_createElementBlock","$slots","_toDisplayString","_Fragment","_withDirectives","_createElementVNode","_renderSlot","_openBlock"],"mappings":";;;;;;;;;;;;;;;;;;;;AAcA,UAAM,WAAW,YAAA;AACjB,UAAM,UAAU,yBAAyB;AAEzC,cAAU,MAAM;AACf,gCAA0B,SAAS,KAAK;AAAA,IACzC,CAAC;AAED,oBAAgB,MAAM;AACrB,UAAI,SAAS,OAAO;AACnB,kCAA0B,KAAK;AAAA,MAChC;AAAA,IACD,CAAC;AAOD,aAAS,0BAA0B,OAAgB,MAAM;AACxD,YAAM,sBAAsB,SAAS,cAA2B,wCAAwC;AACxG,UAAI,qBAAqB;AACxB,4BAAoB,MAAM,UAAU,OAAO,SAAS;AAGpD,YAAI,SAAS,MAAM;AAClB,eAAK,qBAAqB,EAAE,MAAM,MAAA,CAAO;AAAA,QAC1C;AAAA,MACD;AAAA,IACD;;0BAICA,YASWC,MAAA,QAAA,GAAA;AAAA,QART,cAAYA,MAAA,CAAA,EAAC,qBAAA;AAAA,QACd,OAAKC,eAAA,CAAC,sBAAoB,EAAA,8BACcD,MAAA,QAAA,EAAA,CAAQ,CAAA;AAAA,QAC/C,OAAOA,MAAA,CAAA,EAAC,qBAAA;AAAA,QACT,SAAQ;AAAA,MAAA;QACG,cACV,MAAsD;AAAA,UAAtDE,YAAsDF,MAAA,gBAAA,GAAA;AAAA,YAApC,aAAA;AAAA,YAAa,MAAMA,MAAA,aAAA;AAAA,UAAA;;;;;;;;;;;;;;;;;AChCxC,MAAM,iBAAiB,WAAW,WAAW,EAAE,QAAA,EAAU,MAAA;AACzD,MAAM,eAAgB,gBAAA,EAAsD,SAAS,QAAQ;;;;;;;;;;;;;;;;;;AA0B7F,UAAM,cAAcG,kBAAqB,aAAgC;AA2EzE,UAAMC,SAAO;AA0Bb,UAAM,UAAU,WAAA;AAChB,UAAM,mBAAmB,oBAAA;AACzB,UAAM,WAAW,YAAA;AAEjB,UAAM,UAAU,kBAAA;AAChB,UAAM,QAAQ;AAAA;AAAA,MAEb,MAAM,QAAA,eAAe,SAAY,QAAQ;AAAA,MACzC;AAAA,QACC,WAAW,QAAQ,WAAW;AAC7B,gBAAM,YAAY;AAClB,gBAAM,YAAY;AAClB,cAAI,KAAK,IAAI,MAAM,QAAQ,KAAK,IAAI,WAAW;AAC9C,gBAAI,MAAM,YAAY,IAAK,YAAY,KAAM,cAAc,SAAS;AACnEC,mBAAa,qBAAqB;AAAA,gBACjC,MAAM;AAAA,cAAA,CACN;AAAA,YACF,WAAW,MAAM,YAAY,IAAI,YAAY,OAAO,cAAc,QAAQ;AACzEA,mBAAa,qBAAqB;AAAA,gBACjC,MAAM;AAAA,cAAA,CACN;AAAA,YACF;AAAA,UACD;AAAA,QACD;AAAA,MAAA;AAAA,IACD;AAMD,UAAM,eAAe,IAAA;AAErB,UAAM,eAAe,SAAS,MAAM;AAEnC,UAAI,QAAA,kBAAkB,IAAI;AACzB,eAAO,kBAAkB,QAAA,aAAa;AAAA,MACvC;AAEA,aAAO,kBAAkB,OAAO;AAAA,IACjC,CAAC;AAED,UAAM,eAAe,SAAS,OAAO;AAAA,MACpC,MAAM;AAAA,QACL,MAAM,QAAA;AAAA,QACN,KAAK,QAAA;AAAA,QACL,KAAK,QAAA;AAAA,MAAA;AAAA;AAAA;AAAA,MAKN,SAAS;AAAA,QACR,MAAM,MAAM,QAAA;AAAA,QACZ,KAAK,MAAM,QAAA;AAAA,QACX,KAAK,MAAM,QAAA;AAAA,MAAA;AAAA,IACZ,EACC;AAEF,UAAM,kBAAkB,SAAS,MAAM;AACtC,UAAI,aAAa,OAAO;AACvB,eAAO,MAAM,aAAa;AAAA,MAC3B;AACA,aAAO,aAAa,MAAM,QAAQ;AAAA,IACnC,CAAC;AAED,UAAM,gBAAgB,SAAwB,MAAM;AACnD,YAAM,8BAAc,IAAA;AACpB,UAAI,QAAA,WAAW;AAGd,mBAAW,QAAQ,QAAA,UAAU,MAAM,KAAK,GAAG;AAC1C,kBAAQ,IAAI,IAAI;AAAA,QACjB;AAAA,MACD,WAAW,QAAA,aAAa;AAIvB,mBAAW,QAAQ,QAAA,YAAY,MAAM,KAAK,GAAG;AAC5C,kBAAQ,IAAI,IAAI;AAAA,QACjB;AAEA,YAAI,QAAQ,OAAO,GAAG;AACrB,kBAAQ,IAAI,gBAAgB;AAAA,QAC7B;AAAA,MACD,OAAO;AACN,eAAO;AAAA,MACR;AAEA,cAAQ,IAAI,YAAY;AACxB,aAAO,CAAC,GAAG,QAAQ,QAAQ,EAAE,KAAK,KAAK;AAAA,IACxC,CAAC;AAED,UAAM,eAAe,MAAM;AAC1B,UAAI,cAAc,UAAU,MAAM;AACjC,iBAAS,QAAQ,cAAc;AAAA,MAChC;AAAA,IACD,GAAG,EAAE,WAAW,MAAM;AAEtB,UAAM,MAAM,uBAAe,mBAAmB,EAAE,WAAW,MAAM;AAEjE,cAAU,iBAAiB;AAO3B,aAAS,iBAAiB,OAAqC;AAC9D,YAAM,OAAO,KAAK,MAAM,MAAM,MAAM,CAAC,EAAE,IAAI;AAC3C,qBAAe,QAAQ,aAAa,OAAO,KAAK,UAAU,IAAI,CAAC;AAC/D,mBAAa,QAAQ;AACrBD,aAAK,cAAc,EAAE,MAAM;AAC3B,aAAO,MAAM,8BAA8B,EAAE,cAAc,MAAM;AAAA,IAClE;AAOA,aAAS,oBAA0B;AAClC,YAAM,OAAO,SAAS,eAAe,QAAQ,aAAa,KAAK,KAAK,IAAI,EAAE;AAC1E,UAAI,CAAC,MAAM,IAAI,KAAK,SAAS,aAAa,OAAO;AAChD,eAAO,MAAM,8BAA8B,EAAE,cAAc,MAAM;AACjE,qBAAa,QAAQ;AAAA,MACtB;AAAA,IACD;;0BAICE,mBAmDO,QAAA;AAAA,QAnDD,IAAG;AAAA,QAAkB,OAAKL,eAAA,CAAC,0BAAwB,EAAA,yBAAA,CAAA,CAAsCM,KAAAA,OAAO,MAAI,CAAA;AAAA,MAAA;QAC/F,QAAA,4BAAVD,mBAEK,MAFL,YAEKE,gBADD,QAAA,WAAW,GAAA,CAAA;QAGGD,CAAAA,CAAAA,KAAAA,OAAO,qBAAzBD,mBA4CWG,UAAA,EAAA,KAAA,KAAA;AAAA,UAzCHT,MAAA,QAAA,KAAY,QAAA,WAAM,2BADzBM,mBAcM,OAAA;AAAA;YAZL,uBAAM,qDAAmD;AAAA,mDACL,YAAA;AAAA,iDAAqD,YAAA;AAAA,6CAAiDN,MAAA,QAAA;AAAA,YAAA;;YAKzH,YAAA,sBAAjCD,YAA0F,2BAAA;AAAA;cAA3C,6DAAoB,YAAA,QAAW,OAAA,CAAA,QAAA,SAAA,CAAA;AAAA,YAAA;YAE9EW,eAAAC,mBAEM,OAFN,YAEM;AAAA,cADLC,WAAoB,KAAA,QAAA,QAAA,CAAA,GAAA,QAAA,IAAA;AAAA,YAAA;uBADP,YAAA,KAAW;AAAA,YAAA;YAGb,YAAA,QAAZA,WAA2B,KAAA,QAAA,WAAA,IAAA,QAAA,MAAA,CAAA;mBAEZ,QAAA,+BAA+B,QAAA,WAAM,sBAArDC,UAAA,GAAAP,mBA0BM,OA1BN,YA0BM;AAAA,YAzBLJ,YAwBaF,MAAA,UAAA,GAAA;AAAA,cAvBX,YAAY,QAAA,WAAM;AAAA,cACnB,uBAAM,iBAAe;AAAA,0CACqB,QAAA,WAAM;AAAA,wCAAuD,QAAA,WAAM;AAAA,cAAA;cAI5G,KAAKA,MAAA,KAAA;AAAA,cACL,WAAS;AAAA,YAAA;+BACV,MAMO;AAAA,gBANPE,YAMOF,MAAA,IAAA,GAAA;AAAA,kBALN,OAAM;AAAA,kBACL,MAAM,aAAA,SAAgB,mBAAa,KAAK;AAAA,kBACxC,SAAS,aAAA,MAAa,KAAK;AAAA,kBAC3B,SAAS,aAAA,MAAa,KAAK;AAAA,gBAAA;mCAC5B,MAAoB;AAAA,oBAApBY,WAAoB,KAAA,QAAA,QAAA,CAAA,GAAA,QAAA,IAAA;AAAA,kBAAA;;;gBAGrBV,YAMOF,MAAA,IAAA,GAAA;AAAA,kBALN,OAAM;AAAA,kBACL,MAAM,gBAAA;AAAA,kBACN,SAAS,aAAA,MAAa,QAAQ;AAAA,kBAC9B,SAAS,aAAA,MAAa,QAAQ;AAAA,gBAAA;mCAC/B,MAAQ;AAAA,oBAARY,WAAQ,KAAA,QAAA,WAAA,CAAA,GAAA,QAAA,IAAA;AAAA,kBAAA;;;;;;;;QAKCL,CAAAA,KAAAA,OAAO,OAApBK,WAA4B,KAAA,QAAA,WAAA,CAAA,GAAA,QAAA,MAAA,CAAA;;;;;;"}