UNPKG

@nextcloud/vue

Version:
379 lines (378 loc) 13.6 kB
import '../assets/NcAppContent-HF21N7dO.css'; import { getBuilder } from "@nextcloud/browser-storage"; import { getCapabilities } from "@nextcloud/capabilities"; import { emit } from "@nextcloud/event-bus"; import { useSwipe } from "@vueuse/core"; import { Splitpanes, Pane } from "splitpanes"; import { defineComponent, watch, onMounted, onBeforeUnmount, createBlock, openBlock, unref, normalizeClass, withCtx, createVNode, resolveComponent, createElementBlock, createCommentVNode, renderSlot, toDisplayString, Fragment, withDirectives, withModifiers, createElementVNode, vShow } from "vue"; import { m as mdiArrowRight } from "./mdi-XFJRiRqJ.mjs"; import { useIsMobile } from "../composables/useIsMobile/index.mjs"; import { r as register, a as t } from "./_l10n-DrTiip5c.mjs"; import { N as NcButton } from "./NcButton-Dc8V4Urj.mjs"; import { N as NcIconSvgWrapper } from "./NcIconSvgWrapper-BvLanNaW.mjs"; import { _ as _export_sfc } from "./_plugin-vue_export-helper-1tPrXgE0.mjs"; import { g as getLocalizedAppName, A as APP_NAME } from "./appName-DtnLUijR.mjs"; import { l as logger } from "./logger-D3RVzcfQ.mjs"; import { i as isRtl } from "./rtl-v0UOPAM7.mjs"; import "splitpanes/dist/splitpanes.css"; register(); const _sfc_main$1 = /* @__PURE__ */ defineComponent({ __name: "NcAppContentDetailsToggle", setup(__props) { const isMobile = useIsMobile(); watch(isMobile, toggleAppNavigationButton); onMounted(() => { toggleAppNavigationButton(isMobile.value); }); onBeforeUnmount(() => { if (isMobile.value) { toggleAppNavigationButton(false); } }); function toggleAppNavigationButton(hide = true) { const appNavigationToggle = document.querySelector(".app-navigation .app-navigation-toggle"); if (appNavigationToggle) { appNavigationToggle.style.display = hide ? "none" : ""; if (hide === true) { emit("toggle-navigation", { open: false }); } } } return (_ctx, _cache) => { return openBlock(), createBlock(unref(NcButton), { "aria-label": unref(t)("Go back to the list"), class: normalizeClass(["app-details-toggle", { "app-details-toggle--mobile": unref(isMobile) }]), title: unref(t)("Go back to the list"), variant: "tertiary" }, { icon: withCtx(() => [ createVNode(unref(NcIconSvgWrapper), { directional: "", path: unref(mdiArrowRight) }, null, 8, ["path"]) ]), _: 1 }, 8, ["aria-label", "class", "title"]); }; } }); const NcAppContentDetailsToggle = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["__scopeId", "data-v-a28923a1"]]); const browserStorage = getBuilder("nextcloud").persist().build(); const instanceName = getCapabilities().theming?.name ?? "Nextcloud"; const _sfc_main = { name: "NcAppContent", components: { NcAppContentDetailsToggle, Pane, Splitpanes }, props: { /** * Allows to disable the control by swipe of the app navigation open state. */ disableSwipe: { type: Boolean, default: false }, /** * Allows you to set the default width of the resizable list in % on vertical-split * or respectively the default height on horizontal-split. * * Must be between `listMinWidth` and `listMaxWidth`. */ listSize: { type: Number, default: 20 }, /** * Allows you to set the minimum width of the list column in % on vertical-split * or respectively the minimum height on horizontal-split. */ listMinWidth: { type: Number, default: 15 }, /** * Allows you to set the maximum width of the list column in % on vertical-split * or respectively the maximum height on horizontal-split. */ listMaxWidth: { type: Number, default: 40 }, /** * Specify the config key for the pane config sizes * Default is the global var appName if you use the webpack-vue-config */ paneConfigKey: { type: String, default: "" }, /** * When in mobile view, only the list or the details are shown. * * If you provide a list, you need to provide a variable * that will be set to true by the user when an element of * the list gets selected. The details will then show a back * arrow to return to the list that will update this prop to false. */ showDetails: { type: Boolean, default: true }, /** * Content layout used when there is a list together with content: * - `vertical-split` - a 2-column layout with list and default content separated vertically * - `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. * - 'horizontal-split' - a 2-column layout with list and default content separated horizontally * On mobile screen `no-split` layout is forced. */ layout: { type: String, default: "vertical-split", validator(value) { return ["no-split", "vertical-split", "horizontal-split"].includes(value); } }, /** * Specify the `<h1>` page heading */ pageHeading: { type: String, default: null }, /** * Allow setting the page's `<title>` * * If a page heading is set it defaults to `{pageHeading} - {appName} - {instanceName}` e.g. `Favorites - Files - MyPersonalCloud`. * 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`. * When setting the prop then the following format will be used: `{pageTitle} - {instanceName}` */ pageTitle: { type: String, default: null } }, emits: [ "update:showDetails", "resizeList" ], setup() { return { isMobile: useIsMobile(), isRtl }; }, data() { return { contentHeight: 0, swiping: {}, listPaneSize: this.restorePaneConfig() }; }, computed: { paneConfigID() { if (this.paneConfigKey !== "") { return `pane-list-size-${this.paneConfigKey}`; } try { return `pane-list-size-${APP_NAME}`; } catch { logger.info("[NcAppContent]: falling back to global nextcloud pane config"); return "pane-list-size-nextcloud"; } }, detailsPaneSize() { if (this.listPaneSize) { return 100 - this.listPaneSize; } return this.paneDefaults.details.size; }, paneDefaults() { return { list: { size: this.listSize, min: this.listMinWidth, max: this.listMaxWidth }, // set the inverse values of the details column // based on the provided (or default) values of the list column details: { size: 100 - this.listSize, min: 100 - this.listMaxWidth, max: 100 - this.listMinWidth } }; }, realPageTitle() { const entries = /* @__PURE__ */ new Set(); if (this.pageTitle) { for (const part of this.pageTitle.split(" - ")) { entries.add(part); } } else if (this.pageHeading) { for (const part of this.pageHeading.split(" - ")) { entries.add(part); } if (entries.size > 0) { entries.add(getLocalizedAppName()); } } else { return null; } entries.add(instanceName); return [...entries.values()].join(" - "); } }, watch: { realPageTitle: { immediate: true, handler() { if (this.realPageTitle !== null) { document.title = this.realPageTitle; } } }, paneConfigKey: { immediate: true, handler() { this.restorePaneConfig(); } } }, mounted() { if (!this.disableSwipe) { this.swiping = useSwipe(this.$el, { onSwipeEnd: this.handleSwipe }); } this.restorePaneConfig(); }, methods: { /** * handle the swipe event * * @param {TouchEvent} e The touch event * @param {import('@vueuse/core').SwipeDirection} direction The swipe direction of the event */ handleSwipe(e, direction) { const minSwipeX = 70; const touchZone = 300; if (Math.abs(this.swiping.lengthX) > minSwipeX) { if (this.swiping.coordsStart.x < touchZone / 2 && direction === "right") { emit("toggle-navigation", { open: true }); } else if (this.swiping.coordsStart.x < touchZone * 1.5 && direction === "left") { emit("toggle-navigation", { open: false }); } } }, handlePaneResize(event) { const listPaneSize = parseInt(event.panes[0].size, 10); browserStorage.setItem(this.paneConfigID, JSON.stringify(listPaneSize)); this.listPaneSize = listPaneSize; this.$emit("resizeList", { size: listPaneSize }); logger.debug("[NcAppContent] pane config", { listPaneSize }); }, // browserStorage is not reactive, we need to update this manually restorePaneConfig() { const listPaneSize = parseInt(browserStorage.getItem(this.paneConfigID), 10); if (!isNaN(listPaneSize) && listPaneSize !== this.listPaneSize) { logger.debug("[NcAppContent] pane config", { listPaneSize }); this.listPaneSize = listPaneSize; return listPaneSize; } }, /** * The user clicked the back arrow from the details view */ hideDetails() { this.$emit("update:showDetails", false); } } }; const _hoisted_1 = { key: 0, class: "hidden-visually" }; const _hoisted_2 = { key: 1, class: "app-content-wrapper" }; function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { const _component_NcAppContentDetailsToggle = resolveComponent("NcAppContentDetailsToggle"); const _component_Pane = resolveComponent("Pane"); const _component_Splitpanes = resolveComponent("Splitpanes"); return openBlock(), createElementBlock("main", { id: "app-content-vue", class: normalizeClass(["app-content no-snapper", { "app-content--has-list": !!_ctx.$slots.list }]) }, [ $props.pageHeading ? (openBlock(), createElementBlock("h1", _hoisted_1, toDisplayString($props.pageHeading), 1)) : createCommentVNode("", true), !!_ctx.$slots.list ? (openBlock(), createElementBlock(Fragment, { key: 1 }, [ $setup.isMobile || $props.layout === "no-split" ? (openBlock(), createElementBlock("div", { key: 0, class: normalizeClass(["app-content-wrapper app-content-wrapper--no-split", { "app-content-wrapper--show-details": $props.showDetails, "app-content-wrapper--show-list": !$props.showDetails, "app-content-wrapper--mobile": $setup.isMobile }]) }, [ $props.showDetails ? (openBlock(), createBlock(_component_NcAppContentDetailsToggle, { key: 0, onClick: withModifiers($options.hideDetails, ["stop", "prevent"]) }, null, 8, ["onClick"])) : createCommentVNode("", true), withDirectives(createElementVNode("div", null, [ renderSlot(_ctx.$slots, "list", {}, void 0, true) ], 512), [ [vShow, !$props.showDetails] ]), $props.showDetails ? renderSlot(_ctx.$slots, "default", { key: 1 }, void 0, true) : createCommentVNode("", true) ], 2)) : $props.layout === "vertical-split" || $props.layout === "horizontal-split" ? (openBlock(), createElementBlock("div", _hoisted_2, [ createVNode(_component_Splitpanes, { horizontal: $props.layout === "horizontal-split", class: normalizeClass(["default-theme", { "splitpanes--horizontal": $props.layout === "horizontal-split", "splitpanes--vertical": $props.layout === "vertical-split" }]), rtl: $setup.isRtl, onResized: $options.handlePaneResize }, { default: withCtx(() => [ createVNode(_component_Pane, { class: "splitpanes__pane-list", size: $data.listPaneSize || $options.paneDefaults.list.size, "min-size": $options.paneDefaults.list.min, "max-size": $options.paneDefaults.list.max }, { default: withCtx(() => [ renderSlot(_ctx.$slots, "list", {}, void 0, true) ]), _: 3 }, 8, ["size", "min-size", "max-size"]), createVNode(_component_Pane, { class: "splitpanes__pane-details", size: $options.detailsPaneSize, "min-size": $options.paneDefaults.details.min, "max-size": $options.paneDefaults.details.max }, { default: withCtx(() => [ renderSlot(_ctx.$slots, "default", {}, void 0, true) ]), _: 3 }, 8, ["size", "min-size", "max-size"]) ]), _: 3 }, 8, ["horizontal", "class", "rtl", "onResized"]) ])) : createCommentVNode("", true) ], 64)) : createCommentVNode("", true), !_ctx.$slots.list ? renderSlot(_ctx.$slots, "default", { key: 2 }, void 0, true) : createCommentVNode("", true) ], 2); } const NcAppContent = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-a2641cc2"]]); export { NcAppContent as N }; //# sourceMappingURL=NcAppContent-CTpYDkuT.mjs.map