From ef7c01b7875e22cd30c879a91839dccd843105bb Mon Sep 17 00:00:00 2001 From: Sharon Stratsianis Date: Tue, 27 Oct 2026 22:14:48 +1100 Subject: [PATCH 1/5] first pass at a mobile view --- package.json | 2 +- .../FolderPaneContainer.styles.css | 26 ++- .../FolderPaneContainer.ts | 2 +- .../FolderPaneContent.styles.css | 5 + .../FolderPaneSidebar.styles.css | 3 +- .../folder-pane-sidebar/FolderPaneSidebar.ts | 6 +- .../FolderPaneStorageHeader.styles.css | 158 +++++++++++++++++- .../FolderPaneStorageHeader.ts | 59 ++++++- .../folder-pane-view/FolderPaneView.ts | 15 +- src/styles/folderPane.css | 20 ++- 10 files changed, 260 insertions(+), 36 deletions(-) diff --git a/package.json b/package.json index 185071e..282a833 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "lint-fix": "eslint --fix", "typecheck": "tsc --noEmit", "test": "vitest run", - "prepublishOnly": "npm run build && npm run lint", + "prepublishOnly": "npm run build && npm run lint && npm run test", "preversion": "npm run typecheck && npm run lint", "postpublish": "git push origin staging", "start": "vite" diff --git a/src/components/folder-pane-container/FolderPaneContainer.styles.css b/src/components/folder-pane-container/FolderPaneContainer.styles.css index 56789dc..c3c9da6 100644 --- a/src/components/folder-pane-container/FolderPaneContainer.styles.css +++ b/src/components/folder-pane-container/FolderPaneContainer.styles.css @@ -65,10 +65,10 @@ .resource-grid { display: flex; + align-items: center; flex-wrap: wrap; flex: 1 1 auto; align-content: flex-start; - align-items: flex-start; gap: 20px; width: 100%; min-height: 0; @@ -83,9 +83,10 @@ position: relative; display: flex; flex-direction: column; - flex: 0 0 auto; - width: 206px; - max-width: 100%; + flex: 1 1 206px; + width: min(206px, 100%); + min-width: 0; + max-width: 206px; height: 145.824px; border-radius: 5px; border: 1px solid var(--solid-ui-color-gray-200, #cbd5e1); @@ -197,6 +198,23 @@ outline-offset: -2px; } + @media (max-width: 768px) { + .resource-grid { + gap: 15px; + } + + .resource-grid, + .resource-list { + padding: 0; + } + + .resource-grid-item { + flex: 0 0 calc((100% - 15px) / 2); + width: calc((100% - 15px) / 2); + max-width: none; + } + } + .resource-list-main { display: flex; align-items: center; diff --git a/src/components/folder-pane-container/FolderPaneContainer.ts b/src/components/folder-pane-container/FolderPaneContainer.ts index 4d9200e..9419bad 100644 --- a/src/components/folder-pane-container/FolderPaneContainer.ts +++ b/src/components/folder-pane-container/FolderPaneContainer.ts @@ -110,7 +110,7 @@ export default class FolderPaneContainer extends WebComponent { private isRootSpecialFolder (resource: Resource) { if (!resource.isContainer) return false const label = utils.label(resource.subject).toLowerCase() - return label === 'public' || label === 'trash' + return label === 'public' } private getResourcesFromStore () { diff --git a/src/components/folder-pane-content/FolderPaneContent.styles.css b/src/components/folder-pane-content/FolderPaneContent.styles.css index 33d3d16..0e624d4 100644 --- a/src/components/folder-pane-content/FolderPaneContent.styles.css +++ b/src/components/folder-pane-content/FolderPaneContent.styles.css @@ -2,6 +2,11 @@ display: flex; flex-direction: column; flex: 1 1 0%; + padding: 10px 16px; + align-items: flex-start; + gap: 20px; + flex-shrink: 0; + align-self: stretch; width: 100%; min-width: 0; min-height: 0; diff --git a/src/components/folder-pane-sidebar/FolderPaneSidebar.styles.css b/src/components/folder-pane-sidebar/FolderPaneSidebar.styles.css index d1fcd42..3f4b305 100644 --- a/src/components/folder-pane-sidebar/FolderPaneSidebar.styles.css +++ b/src/components/folder-pane-sidebar/FolderPaneSidebar.styles.css @@ -122,8 +122,7 @@ } .resource-row-special icon-lucide-folder, - .resource-row-special icon-lucide-globe, - .resource-row-special icon-lucide-trash-2 { + .resource-row-special icon-lucide-globe { width: 16px; height: 16px; flex-shrink: 0; diff --git a/src/components/folder-pane-sidebar/FolderPaneSidebar.ts b/src/components/folder-pane-sidebar/FolderPaneSidebar.ts index d39f8e2..f15ef76 100644 --- a/src/components/folder-pane-sidebar/FolderPaneSidebar.ts +++ b/src/components/folder-pane-sidebar/FolderPaneSidebar.ts @@ -10,11 +10,10 @@ import '~icons/lucide/chevron-right' import '~icons/lucide/circle-small' import '~icons/lucide/folder' import '~icons/lucide/globe' -import '~icons/lucide/trash-2' import { getResourcesForContainer, type FolderPaneResourceLogic } from '../../helpers' import * as debug from '../../debug' -type SpecialIcon = 'folder' | 'globe' | 'trash' +type SpecialIcon = 'folder' | 'globe' @customElement('folder-pane-sidebar') export default class FolderPaneSidebar extends WebComponent { @@ -203,7 +202,6 @@ export default class FolderPaneSidebar extends WebComponent { private renderSpecialIcon (icon: SpecialIcon) { if (icon === 'globe') return html`` - if (icon === 'trash') return html`` return html`` } @@ -247,7 +245,6 @@ export default class FolderPaneSidebar extends WebComponent { return index >= 0 ? orderedResources.splice(index, 1)[0] : null } const publicResource = takeNamed('public') - const trashResource = takeNamed('trash') return html` ` } diff --git a/src/components/folder-pane-storage-header/FolderPaneStorageHeader.styles.css b/src/components/folder-pane-storage-header/FolderPaneStorageHeader.styles.css index ee66177..b93ff9d 100644 --- a/src/components/folder-pane-storage-header/FolderPaneStorageHeader.styles.css +++ b/src/components/folder-pane-storage-header/FolderPaneStorageHeader.styles.css @@ -15,6 +15,43 @@ padding: 10px 0; } +.folder-pane-storage-header-locations { + display: none; + align-items: center; + gap: 4px; + flex: 0 0 auto; +} + +.folder-pane-storage-header-locations button { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 5.6px; + padding: 5.6px 8px; + border: 0; + background: transparent; + color: var(--solid-ui-color-gray-600, #4a5565); + cursor: pointer; + font: inherit; + font-size: var(--solid-ui-font-size-sm, 0.875rem); + font-weight: 500; + text-align: center; + border-bottom: 2px solid transparent; +} + +.folder-pane-storage-header-locations button[aria-current='page'] { + border-bottom-color: var(--solid-ui-color-primary, #7c4dff); + color: var(--solid-ui-color-primary, #7c4dff); + font-weight: 600; +} + +.folder-pane-storage-location icon-lucide-folder, +.folder-pane-storage-location icon-lucide-globe { + width: 1rem; + height: 1rem; + flex: 0 0 auto; +} + .folder-pane-storage-header-breadcrumbs, .folder-pane-storage-header-breadcrumbs ol, .folder-pane-storage-header-breadcrumbs li, @@ -26,7 +63,7 @@ .folder-pane-storage-header-breadcrumbs { min-width: 0; - gap: 0.35rem; + gap: 5.6px; overflow-x: auto; overflow-y: hidden; } @@ -41,7 +78,7 @@ .folder-pane-storage-header-breadcrumbs ol { min-width: 0; flex-wrap: nowrap; - gap: 0.35rem; + gap: 5.6px; margin: 0; padding: 0; list-style: none; @@ -51,7 +88,7 @@ .folder-pane-storage-header-breadcrumbs li { min-width: 0; - gap: 0.35rem; + gap: 5.6px; } .folder-pane-storage-header-breadcrumbs button { @@ -75,17 +112,17 @@ .folder-pane-storage-header-toolbar { flex: 0 0 auto; justify-content: flex-end; - gap: 0.5rem; + gap: 8px; min-width: 0; } .folder-pane-storage-header-search { display: none; align-items: center; - gap: 0.35rem; + gap: 5.6px; min-width: 8rem; max-width: 14rem; - padding: 0.25rem 0.5rem; + padding: 4px 8px; border: 1px solid var(--solid-ui-color-gray-300, #d1d5dc); border-radius: 0.35rem; color: var(--solid-ui-color-gray-500, #6a7282); @@ -158,6 +195,42 @@ margin-left: 22px; } +.folder-pane-storage-header-add-trigger { + display: inline-flex; + align-items: center; +} + +.folder-pane-storage-header-add-button { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 5.6px; + min-height: 36px; + padding: 5.6px 12px; + border: 0; + border-radius: 5.6px; + background: var(--solid-ui-color-primary, #7c4dff); + color: var(--solid-ui-color-white, #ffffff); + cursor: pointer; + font: inherit; +} + +.folder-pane-storage-header-add-button:hover { + background: var(--solid-ui-color-primary-hover, #b9a2ff); +} + +.folder-pane-storage-header-add-button:focus-visible { + outline: 2px solid var(--solid-ui-color-primary, #7c4dff); + outline-offset: 2px; +} + +.folder-pane-storage-header-add-button icon-lucide-plus, +.folder-pane-storage-header-add-chevron { + width: 1.25rem; + height: 1.25rem; + flex: 0 0 auto; +} + .folder-pane-storage-header-add-menu img[slot="left-icon"] { display: block; width: 1.25rem; @@ -170,3 +243,76 @@ width: 13.333px; height: 12.664px; } + +@media (max-width: 768px) { + .folder-pane-storage-header { + flex-wrap: wrap; + gap: 8px; + border-bottom: 1px solid var(--solid-ui-color-gray-200, #e5e7eb); + } + + .folder-pane-storage-header-breadcrumbs { + display: none; + } + + .folder-pane-storage-header-locations { + display: flex; + width: 100%; + } + + .folder-pane-storage-header-breadcrumbs { + flex: 1 1 auto; + max-width: 100%; + } + + .folder-pane-storage-header-toolbar { + flex: 0 1 auto; + margin-left: auto; + } + + .folder-pane-storage-header-add-menu { + margin: 0; + } + + .folder-pane-storage-header-add-trigger { + position: fixed; + right: 30px; + bottom: 50px; + z-index: 200; + display: flex; + width: 50px; + height: 50px; + box-sizing: border-box; + padding: 9.722px 13.889px; + justify-content: center; + align-items: center; + gap: 11.111px; + aspect-ratio: 1 / 1; + border-radius: 6.944px; + background: var(--solid-ui-color-primary, #7c4dff); + box-shadow: 1.389px 5.556px 9.722px 0 rgb(0 0 0 / 56%); + } + + .folder-pane-storage-header-add-button { + width: 100%; + height: 100%; + box-sizing: border-box; + min-height: 0; + padding: 0; + background: transparent; + } + + .folder-pane-storage-header-add-button:hover { + background: transparent; + } + + .folder-pane-storage-header-add-button icon-lucide-plus { + width: 20px; + height: 20px; + } + + .folder-pane-storage-header-add-label, + .folder-pane-storage-header-add-chevron { + display: none; + } +} diff --git a/src/components/folder-pane-storage-header/FolderPaneStorageHeader.ts b/src/components/folder-pane-storage-header/FolderPaneStorageHeader.ts index 59e8639..c208ffc 100644 --- a/src/components/folder-pane-storage-header/FolderPaneStorageHeader.ts +++ b/src/components/folder-pane-storage-header/FolderPaneStorageHeader.ts @@ -8,6 +8,8 @@ import 'solid-ui/components/button' import 'solid-ui/components/menu' import 'solid-ui/components/menu-item' import '~icons/lucide/folder-open' +import '~icons/lucide/folder' +import '~icons/lucide/globe' import '~icons/lucide/layout-grid' import '~icons/lucide/list' import '~icons/lucide/chevron-down' @@ -28,6 +30,9 @@ export default class FolderPaneStorageHeader extends WebComponent { @property({ attribute: false }) accessor selectedResource: NamedNode | undefined = undefined + @property({ attribute: false }) + accessor publicResource: NamedNode | undefined = undefined + @property({ attribute: false }) accessor browserContext: DataBrowserContext | undefined = undefined @@ -118,6 +123,23 @@ export default class FolderPaneStorageHeader extends WebComponent { })) } + private isWithin (resource: NamedNode | undefined, ancestor: NamedNode | undefined) { + if (!resource || !ancestor) return false + let current: NamedNode | undefined = resource + for (let depth = 0; current && depth < 64; depth++) { + if (current.sameTerm(ancestor)) return true + const parent = current.dir() + if (!parent || parent.sameTerm(current)) break + current = parent + } + return false + } + + private isHomeSelected () { + return this.isWithin(this.selectedResource, this.subject) && + !this.isWithin(this.selectedResource, this.publicResource) + } + private updateSearch (event: Event) { this.dispatchEvent(new CustomEvent('folder-pane-search', { detail: { query: (event.target as HTMLInputElement).value }, @@ -137,6 +159,22 @@ export default class FolderPaneStorageHeader extends WebComponent { render () { return html`
+