-
Notifications
You must be signed in to change notification settings - Fork 212
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
288 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
<template> | ||
<div | ||
ref="containerRef" | ||
class="relative flex max-w-full sm:-ms-1" | ||
:class="buttonsMargin" | ||
> | ||
<div | ||
class="buttons-container max-w-full" | ||
:class="{ [`m${scrollButtonPosition}-10`]: shouldScroll }" | ||
> | ||
<div | ||
ref="buttonsRef" | ||
class="buttons flex justify-start gap-x-3 overflow-x-scroll sm:gap-x-1" | ||
:class="{ [`faded-overflow-${scrollButtonPosition}`]: shouldScroll }" | ||
> | ||
<VButton | ||
v-if="showCreator" | ||
as="VLink" | ||
size="small" | ||
has-icon-start | ||
class="label-bold" | ||
:variant="buttonVariant" | ||
:href=" | ||
getCollectionPath({ type: mediaType, source: sourceSlug, creator }) | ||
" | ||
> | ||
<VIcon name="person" /><span class="w-max">{{ creator }}</span> | ||
</VButton> | ||
<VButton | ||
as="VLink" | ||
size="small" | ||
has-icon-start | ||
class="label-bold" | ||
:variant="buttonVariant" | ||
:href="getCollectionPath({ type: mediaType, source: sourceSlug })" | ||
><VIcon name="institution" /><span class="w-max">{{ | ||
sourceName | ||
}}</span> | ||
</VButton> | ||
</div> | ||
</div> | ||
<div | ||
v-if="shouldScroll" | ||
class="absolute z-10 h-8 w-8 bg-white" | ||
:class="scrollButton.style" | ||
> | ||
<VIconButton | ||
:icon-props="{ name: scrollButton.icon, rtlFlip: true }" | ||
label="scroll" | ||
variant="transparent-gray" | ||
size="small" | ||
@click="scroll" | ||
/> | ||
</div> | ||
</div> | ||
</template> | ||
|
||
<script lang="ts"> | ||
import { computed, defineComponent, PropType, ref } from "vue" | ||
import { useElementSize, useScroll, watchDebounced } from "@vueuse/core" | ||
import { useUiStore } from "~/stores/ui" | ||
import { useI18n } from "~/composables/use-i18n" | ||
import type { SupportedMediaType } from "~/constants/media" | ||
import VButton from "~/components/VButton.vue" | ||
import VIcon from "~/components/VIcon/VIcon.vue" | ||
import VIconButton from "~/components/VIconButton/VIconButton.vue" | ||
/** | ||
* A link to a collection page, either a source or a creator. | ||
*/ | ||
export default defineComponent({ | ||
name: "VByLine", | ||
components: { VIconButton, VIcon, VButton }, | ||
props: { | ||
creator: { | ||
type: String, | ||
}, | ||
sourceName: { | ||
type: String, | ||
required: true, | ||
}, | ||
sourceSlug: { | ||
type: String, | ||
required: true, | ||
}, | ||
mediaType: { | ||
type: String as PropType<SupportedMediaType>, | ||
required: true, | ||
}, | ||
}, | ||
setup(props) { | ||
const containerRef = ref<HTMLElement | null>(null) | ||
const buttonsRef = ref<HTMLElement | null>(null) | ||
const uiStore = useUiStore() | ||
const buttonVariant = computed(() => | ||
uiStore.isBreakpoint("sm") ? "transparent-gray" : "filled-gray" | ||
) | ||
const showCreator = computed(() => { | ||
return props.creator && props.creator !== "Unidentified" | ||
}) | ||
const shouldScroll = ref(false) | ||
const { x } = useScroll(buttonsRef) | ||
const { width: containerWidth } = useElementSize(containerRef) | ||
const { width: buttonsWidth } = useElementSize(buttonsRef) | ||
watchDebounced( | ||
[buttonsWidth, containerWidth], | ||
() => { | ||
shouldScroll.value = buttonsWidth.value >= containerWidth.value | ||
}, | ||
{ debounce: 500 } | ||
) | ||
const i18n = useI18n() | ||
const dir = computed(() => i18n.localeProperties.dir ?? "ltr") | ||
// end, start | ||
const scrollButtonPosition = ref<"e" | "s">("e") | ||
const scroll = () => { | ||
let scrollValue = x.value ? 0 : buttonsWidth.value | ||
if (dir.value === "rtl") { | ||
scrollValue = -scrollValue | ||
} | ||
buttonsRef.value?.scroll({ | ||
left: scrollValue, | ||
behavior: "smooth", | ||
}) | ||
scrollButtonPosition.value = | ||
scrollButtonPosition.value === "e" ? "s" : "e" | ||
} | ||
watchDebounced( | ||
x, | ||
(xValue) => { | ||
const distFromStart = Math.abs(xValue) | ||
const distFromEnd = | ||
(buttonsRef.value?.scrollWidth ?? 0) - | ||
distFromStart - | ||
containerWidth.value | ||
if (distFromEnd < 1) { | ||
scrollButtonPosition.value = "s" | ||
} else if (distFromStart < 1) scrollButtonPosition.value = "e" | ||
}, | ||
{ debounce: 100 } | ||
) | ||
const buttonsMargin = computed(() => { | ||
return shouldScroll.value ? `p${scrollButtonPosition.value}-8` : "" | ||
}) | ||
const scrollButton = computed(() => { | ||
if (scrollButtonPosition.value === "e") { | ||
return { style: "end-0 ms-2", icon: "chevron-forward" } | ||
} else { | ||
return { style: "start-0 me-2", icon: "chevron-back" } | ||
} | ||
}) | ||
// TODO: implement this function in the search store. | ||
const getCollectionPath = ({ | ||
type, | ||
source, | ||
creator, | ||
}: { | ||
type: SupportedMediaType | ||
source: string | ||
creator?: string | ||
}) => { | ||
console.log(type, source, creator) | ||
return "/" | ||
} | ||
return { | ||
buttonVariant, | ||
showCreator, | ||
containerRef, | ||
buttonsRef, | ||
buttonsMargin, | ||
scroll, | ||
shouldScroll, | ||
scrollButton, | ||
scrollButtonPosition, | ||
getCollectionPath, | ||
} | ||
}, | ||
}) | ||
</script> | ||
|
||
<style scoped> | ||
.faded-overflow-e:dir(ltr), | ||
.faded-overflow-s:dir(rtl) { | ||
mask-image: linear-gradient(to left, transparent, white 20%); | ||
} | ||
.faded-overflow-e:dir(rtl), | ||
.faded-overflow-s:dir(ltr) { | ||
mask-image: linear-gradient(to right, transparent, white 20%); | ||
} | ||
.buttons::-webkit-scrollbar { | ||
width: 0 !important; | ||
} | ||
.buttons { | ||
scrollbar-width: none; | ||
} | ||
</style> |
78 changes: 78 additions & 0 deletions
78
frontend/src/components/VMediaInfo/VByLine/meta/VByLine.stories.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { | ||
ArgsTable, | ||
Canvas, | ||
Description, | ||
Meta, | ||
Story, | ||
} from "@storybook/addon-docs" | ||
import { supportedMediaTypes } from "~/constants/media" | ||
import { useProviderStore } from "~/stores/provider" | ||
|
||
import VByLine from "~/components/VMediaInfo/VByLine/VByLine.vue" | ||
|
||
export const Template = (args) => ({ | ||
template: `<div class="wrapper w-full inline-flex p-4"><VByLine v-bind="args" /></div>`, | ||
components: { VByLine }, | ||
setup() { | ||
const providerStore = useProviderStore() | ||
providerStore.getProviders().then(/** */) | ||
return { args } | ||
}, | ||
}) | ||
|
||
<Meta | ||
title="Components/VByLine" | ||
component={VByLine} | ||
argTypes={{ | ||
mediaType: { | ||
control: "select", | ||
options: supportedMediaTypes, | ||
}, | ||
creator: { | ||
control: "text", | ||
}, | ||
sourceName: { | ||
control: "text", | ||
}, | ||
sourceSlug: { | ||
control: "text", | ||
}, | ||
}} | ||
/> | ||
|
||
# VByLine | ||
|
||
<Description of={VByLine} /> | ||
|
||
<ArgsTable of={VByLine} /> | ||
|
||
<Canvas> | ||
<Story | ||
name="default" | ||
args={{ | ||
mediaType: "image", | ||
creator: "kellascat", | ||
sourceSlug: "met", | ||
sourceName: "Metropolitan Museum", | ||
}} | ||
> | ||
{Template.bind({})} | ||
</Story> | ||
</Canvas> | ||
|
||
## Long titles use scrolling | ||
|
||
<Canvas> | ||
<Story | ||
name="long" | ||
args={{ | ||
mediaType: "image", | ||
creator: "Roland Folger Coffin, American, 1826–1888", | ||
sourceSlug: "smithsonian_cooper_hewitt_museum", | ||
sourceName: | ||
"Smithsonian Institution: Cooper Hewitt, Smithsonian Design Museum", | ||
}} | ||
> | ||
{Template.bind({})} | ||
</Story> | ||
</Canvas> |