Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ResourceTiles on mobile and add basic resize slider for tiles #8372

Merged
merged 6 commits into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog/unreleased/enhancement-resources-tiles-view
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@ Enhancement: Add tiles view for resource display

We've added a switch to change from the known resource table to a tiles view.
The change gets saved to the url and persisted across resource navigation.
Tiles can be dynamically resized on screens bigger than mobile, using the "display customization options" dropdown.

https://github.com/owncloud/web/pull/7991
https://github.com/owncloud/web/pull/8372
https://github.com/owncloud/web/issues/6378
https://github.com/owncloud/web/issues/6379
https://github.com/owncloud/web/issues/6380
https://github.com/owncloud/web/issues/8367
https://github.com/owncloud/web/issues/8368
5 changes: 5 additions & 0 deletions packages/design-system/src/tokens/ods/size.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,8 @@ size:
value: 22px
form-check-default:
value: 14px
tiles:
resize-step:
Copy link
Contributor

@kulmann kulmann Feb 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If resize-step is the only size value for tiles, we could omit the nesting and just have something like

  tiles-step:
    value: 12rem

value: 12rem
default:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default is not in use. Delete it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, thx. It's meant to be used for non-resizable tiles (like the ones on the spaces-projects view), I added the token without actually using it in the component 🙈 see latest commit in this PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aaaah, now it makes sense 😅

value: 14rem
63 changes: 63 additions & 0 deletions packages/web-app-files/src/components/AppBar/ViewOptions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,21 @@
@change="itemsPerPage = $event"
/>
</li>
<li
v-if="viewModeCurrent === ViewModeConstants.tilesView.name"
class="files-view-options-list-item oc-mt-m oc-visible@s oc-flex oc-flex-between"
>
<label for="tiles-size-slider" v-text="resizeTilesLabel" />
<input
v-model="viewSizeCurrent"
type="range"
min="1"
max="6"
name="tiles-size-slider"
class="oc-range"
@input="setTilesViewSize"
/>
</li>
</oc-list>
</oc-drop>
</div>
Expand Down Expand Up @@ -91,6 +106,10 @@ export default defineComponent({
name: ViewModeConstants.queryName,
defaultValue: ViewModeConstants.defaultModeName
})
const viewSizeQuery = useRouteQueryPersisted({
name: ViewModeConstants.tilesSizeQueryName,
defaultValue: ViewModeConstants.tilesSizeDefault.toString()
})
watch(
[perPageQuery, viewModeQuery],
(params) => {
Expand All @@ -102,6 +121,7 @@ export default defineComponent({
return {
ViewModeConstants,
viewModeCurrent: viewModeQuery,
viewSizeCurrent: viewSizeQuery,
itemsPerPage: perPageQuery,
queryParamsLoading
}
Expand All @@ -112,6 +132,9 @@ export default defineComponent({
viewOptionsButtonLabel() {
return this.$gettext('Display customization options of the files list')
},
resizeTilesLabel() {
return this.$gettext('Tile size')
},

hiddenFilesShownModel: {
get() {
Expand All @@ -137,6 +160,11 @@ export default defineComponent({
setViewMode(mode) {
this.viewModeCurrent = mode.name
},
setTilesViewSize() {
document
.querySelector(':root')
.style.setProperty(`--oc-size-tiles-resize-step`, `${this.viewSizeCurrent * 12}rem`)
},
updateHiddenFilesShownModel(event) {
this.hiddenFilesShownModel = event
},
Expand Down Expand Up @@ -170,4 +198,39 @@ export default defineComponent({
margin-top: var(--oc-space-small);
}
}

.oc-range {
-webkit-appearance: none;
-webkit-transition: 0.2s;
border-radius: 0.3rem;
background: var(--oc-color-input-border);
height: 0.5rem;
opacity: 0.7;
outline: none;
transition: opacity 0.2s;
width: 100%;
max-width: 50%;

&:hover {
opacity: 1;
}

&::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
background: var(--oc-color-swatch-primary-default);
border-radius: 50%;
cursor: pointer;
height: 1rem;
width: 1rem;
}

&::-moz-range-thumb {
background: var(--oc-color-swatch-primary-default);
border-radius: 50%;
cursor: pointer;
height: 1rem;
width: 1rem;
}
}
</style>
44 changes: 26 additions & 18 deletions packages/web-app-files/src/components/FilesList/ResourceTiles.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
<template>
<div>
<oc-list class="oc-tiles oc-flex" :class="tileWidth === 'small' ? 'small-tiles' : ''">
<li v-for="(resource, index) in data" :key="resource.id" class="oc-tiles-item">
<oc-list class="oc-tiles oc-flex" :class="resizable ? 'resizableTiles' : ''">
<li v-for="resource in data" :key="resource.id" class="oc-tiles-item">
<oc-tile
:ref="
(el) => {
tileRefs.tiles[index] = el
}
"
:ref="(el) => (tileRefs.tiles[resource.id] = el)"
:resource="resource"
:resource-route="getRoute(resource)"
@vue:mounted="$emit('rowMounted', resource, tileRefs.tiles[index], ImageDimension.Tile)"
@contextmenu="showContextMenu($event, index, tileRefs.tiles[index])"
@vue:mounted="
$emit('rowMounted', resource, tileRefs.tiles[resource.id], ImageDimension.Tile)
"
@contextmenu="showContextMenu($event, resource.id, tileRefs.tiles[resource.id])"
@click="emitTileClick(resource)"
>
<template #imageField>
Expand All @@ -26,20 +24,20 @@
:id="`space-context-btn-${resource.getDomSelector()}`"
:ref="
(el) => {
tileRefs.dropBtns[index] = el
tileRefs.dropBtns[resource.id] = el
}
"
v-oc-tooltip="contextMenuLabel"
:aria-label="contextMenuLabel"
appearance="raw"
@click="resetDropPosition($event, index, tileRefs.dropBtns[index])"
@click="resetDropPosition($event, resource.id, tileRefs.dropBtns[resource.id])"
>
<oc-icon name="more-2" />
</oc-button>
<oc-drop
:ref="
(el) => {
tileRefs.dropEls[index] = el
tileRefs.dropEls[resource.id] = el
}
"
:drop-id="`space-context-drop-${resource.getDomSelector()}`"
Expand Down Expand Up @@ -85,9 +83,9 @@ export default defineComponent({
type: Array as PropType<Resource[]>,
default: () => []
},
tileWidth: {
type: String,
default: ''
resizable: {
type: Boolean,
default: false
},
targetRouteCallback: {
type: Function,
Expand Down Expand Up @@ -210,12 +208,22 @@ export default defineComponent({
.oc-tiles {
column-gap: 1rem;
display: grid;
grid-template-columns: repeat(auto-fill, 14rem);
grid-template-columns: repeat(auto-fill, var(--oc-size-tiles-default));
justify-content: flex-start;
row-gap: 1rem;

&.small-tiles {
grid-template-columns: repeat(auto-fill, 12rem);
&.resizableTiles {
grid-template-columns: repeat(auto-fill, var(--oc-size-tiles-resize-step));
}

@media only screen and (max-width: 640px) {
grid-template-columns: 80%;
justify-content: center;
padding: var(--oc-space-medium) 0;

&.resizableTiles {
grid-template-columns: 80%;
}
}
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { Resource } from 'web-client'
import { useSelectedResources, SelectedResourcesResult } from '../selection'
import { ReadOnlyRef } from 'web-pkg'
import { ScrollToResult, useScrollTo } from '../scrollTo'
import { useViewMode, ViewModeConstants } from '../viewMode'
import { useViewMode, useViewSize, ViewModeConstants } from '../viewMode'

interface ResourcesViewDefaultsOptions<T, U extends any[]> {
loadResourcesTask?: Task<T, U>
Expand All @@ -37,7 +37,7 @@ type ResourcesViewDefaultsResult<T, TT, TU extends any[]> = {
sortBy: ReadOnlyRef<string>
sortDir: ReadOnlyRef<SortDir>
viewMode: ReadOnlyRef<string>

viewSize: ReadOnlyRef<string>
selectedResources: Ref<Resource[]>
selectedResourcesIds: Ref<(string | number)[]>
isResourceInSelection(resource: Resource): boolean
Expand Down Expand Up @@ -72,6 +72,10 @@ export const useResourcesViewDefaults = <T, TT, TU extends any[]>(
const currentViewMode = computed((): string => queryItemAsString(currentViewModeQuery.value))
const viewMode = useViewMode(currentViewMode)

const currentTilesSizeQuery = useRouteQuery('tiles-size', '1')
const currentTilesSize = computed((): string => String(currentTilesSizeQuery.value))
const viewSize = useViewSize(currentTilesSize)

const paginationPageQuery = useRouteQuery('page', '1')
const paginationPage = computed((): number => parseInt(String(paginationPageQuery.value)))
const { items: paginatedResources, total: paginationPages } = usePagination({
Expand All @@ -92,6 +96,7 @@ export const useResourcesViewDefaults = <T, TT, TU extends any[]>(
storeItems,
fields,
viewMode,
viewSize,
paginatedResources,
paginationPages,
paginationPage,
Expand Down
2 changes: 2 additions & 0 deletions packages/web-app-files/src/composables/viewMode/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,6 @@ export abstract class ViewModeConstants {
}
}
static readonly queryName: string = 'view-mode'
static readonly tilesSizeDefault: number = 1
static readonly tilesSizeQueryName: string = 'tiles-size'
}
12 changes: 12 additions & 0 deletions packages/web-app-files/src/composables/viewMode/useViewMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,15 @@ export function useViewMode(options: ComputedRef<string>): ComputedRef<string> {
})
return computed(() => queryItemAsString(unref(viewModeQuery)))
}

export function useViewSize<T>(options: ComputedRef<string>): ComputedRef<string> {
if (options) {
return computed(() => unref(options))
}

const viewModeSize = useRouteQueryPersisted({
name: ViewModeConstants.tilesSizeQueryName,
defaultValue: ViewModeConstants.tilesSizeDefault.toString()
})
return computed(() => String(unref(viewModeSize)))
}
2 changes: 1 addition & 1 deletion packages/web-app-files/src/views/spaces/GenericSpace.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
v-else-if="viewMode === ViewModeConstants.tilesView.name"
:data="paginatedResources"
class="oc-px-m oc-pt-l"
tile-width="small"
:resizable="true"
:target-route-callback="resourceTargetRouteCallback"
:space="space"
@rowMounted="rowMounted"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const useResourcesViewDefaultsMock = (
scrollToResource: jest.fn(),
scrollToResourceFromRoute: jest.fn(),
viewMode: ref('resource-table'),
viewSize: ref('1'),
...options
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,32 @@ describe('ViewOptions component', () => {
const viewModeSwitchButtons = wrapper.find('.viewmode-switch-buttons')
expect(viewModeSwitchButtons.html()).toMatchSnapshot()
})
it('displays a tile-resize range slider in dropdown if currentViewMode is resource-tiles', async () => {
const { wrapper } = getWrapper(
{},
{
viewModes: [ViewModeConstants.condensedTable, ViewModeConstants.default]
},
'resource-tiles'
)

const filesViewOptions = wrapper.find('#files-view-options-drop')
expect(filesViewOptions.html()).toMatchSnapshot()
})
})

function getWrapper(
{ perPage = '100' } = {},
props?: {
viewModes: ViewMode[]
}
},
currentViewMode = ''
) {
jest.mocked(useRouteQueryPersisted).mockImplementation(() => ref(perPage))

const storeOptions = { ...defaultStoreMockOptions }
const store = createStore(storeOptions)
const mocks = defaultComponentMocks()
const mocks = { ...defaultComponentMocks(), viewModeCurrent: currentViewMode }
return {
storeOptions,
mocks,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`ViewOptions component displays a tile-resize range slider in dropdown if currentViewMode is resource-tiles 1`] = `
<div class="oc-drop oc-box-shadow-medium oc-rounded oc-width-auto" id="files-view-options-drop">
<div class="oc-card oc-card-body oc-background-secondary oc-p-m">
<ul class="oc-list oc-my-rm oc-mx-rm">
<li class="files-view-options-list-item oc-mb-m">
<span class="oc-switch">
<span id="oc-switch-label-21">Show hidden files</span>
<button aria-checked="false" aria-labelledby="oc-switch-label-21" class="oc-switch-btn" role="switch"></button>
</span>
</li>
<li class="files-view-options-list-item oc-my-m">
<span class="oc-switch">
<span id="oc-switch-label-22">Show file extensions</span>
<button aria-checked="false" aria-labelledby="oc-switch-label-22" class="oc-switch-btn" role="switch"></button>
</span>
</li>
<li class="files-view-options-list-item oc-mt-m">
<div class="oc-page-size files-pagination-size">
<label class="oc-page-size-label" for="oc-page-size-23">Items per page</label>
<oc-select-stub class="oc-page-size-select" clearable="false" disabled="false" filter="[Function]" id="oc-select-24" input-id="oc-page-size-23" loading="false" model-value="100" options="100,500" searchable="false"></oc-select-stub>
</div>
</li>
<li class="files-view-options-list-item oc-mt-m oc-visible@s oc-flex oc-flex-between">
<label for="tiles-size-slider">Tile size</label>
<input class="oc-range" max="6" min="1" name="tiles-size-slider" type="range">
</li>
</ul>
</div>
</div>
`;

exports[`ViewOptions component sets the correct initial files page limit 1`] = `
<div class="oc-flex oc-flex-middle">
<!--v-if-->
Expand All @@ -25,6 +56,7 @@ exports[`ViewOptions component sets the correct initial files page limit 1`] = `
<oc-select-stub class="oc-page-size-select" clearable="false" disabled="false" filter="[Function]" id="oc-select-4" input-id="oc-page-size-3" loading="false" model-value="100" options="100,500" searchable="false"></oc-select-stub>
</div>
</li>
<!--v-if-->
</ul>
</div>
</div>
Expand Down