Skip to content

Commit

Permalink
Template Marketplace (#172)
Browse files Browse the repository at this point in the history
* add new icon to whitelist

* template collection to block directory

* template page wip

* unused css

* add extensions to directory

* wip

* add types

* ditch extensions for now

* new modal component

* template wip

* update hubspot forms

* fix types

* Fix deprecation warning for sass

* render only one HSform based on screen width

* extract image gallery

* update gallery on projects

* add light background transparent images

* remove unneeded var

* (try) to satisfy ts compiler

* add to prerender

* bit of cleanup
  • Loading branch information
bryantgillespie authored Nov 13, 2024
1 parent 75cb87a commit 96d0aa5
Show file tree
Hide file tree
Showing 20 changed files with 3,777 additions and 1,818 deletions.
4 changes: 0 additions & 4 deletions components/Base/ButtonGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,5 @@ withDefaults(defineProps<BaseButtonGroupProps>(), {
padding: var(--space-2);
}
}
:deep(> *) {
flex-shrink: 0;
}
}
</style>
67 changes: 67 additions & 0 deletions components/Base/Gallery.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<script setup lang="ts">
const props = defineProps<{
images: {
uuid: string;
alt?: string;
}[];
}>();
const selectedImage = ref(props.images.length ? props.images[0].uuid : null);
</script>
<template>
<div class="gallery">
<BaseMedia aspect="16-9" class="gallery-selected">
<BaseDirectusImage v-if="selectedImage" :uuid="selectedImage" :alt="''" :width="800" loading="lazy" />
</BaseMedia>
<!-- Show thumnbails ONLY if there are more than one image -->
<BaseCardGroup v-if="images.length > 1" grid="4" class="gallery-thumbnails">
<button
v-for="image in images"
:key="image.uuid as string"
type="button"
class="gallery-image"
:class="selectedImage === image.uuid ? 'selected' : ''"
@click="selectedImage = image.uuid"
>
<BaseDirectusImage :uuid="image.uuid" :alt="image.alt ?? ''" :width="225" :height="125" loading="lazy" />
</button>
</BaseCardGroup>
</div>
</template>
<style lang="scss" scoped>
.gallery-selected {
background-color: var(--gray-50);
border-radius: var(--rounded-lg);
}
.gallery-image {
cursor: pointer;
width: 100%;
overflow: hidden;
border-radius: var(--rounded-lg);
border: 2px solid transparent;
&.selected {
border: 2px solid var(--primary);
}
&:focus {
outline: var(--primary) auto 1px;
}
img {
width: 100%;
height: 100%;
object-fit: cover;
transition: scale var(--duration-300) var(--ease-out);
&:hover {
scale: 1.03;
}
}
}
.gallery-thumbnails {
margin-block-start: var(--space-4);
}
</style>
26 changes: 13 additions & 13 deletions components/Base/HsForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface BaseHsFormProps {
inline?: boolean;
align?: 'left' | 'center';
routeToMeetingLinkOnSuccess?: boolean;
instanceId?: string;
}
const props = withDefaults(defineProps<BaseHsFormProps>(), {
Expand All @@ -13,6 +14,11 @@ const props = withDefaults(defineProps<BaseHsFormProps>(), {
align: 'center',
});
// Nuxt Scripts help prevent the script from being loaded multiple times
const { onLoaded } = useScript({
src: 'https://js.hsforms.net/forms/embed/v2.js',
});
const { formId } = toRefs(props);
const { $directus, $readSingleton, $posthog } = useNuxtApp();
Expand Down Expand Up @@ -78,22 +84,16 @@ const renderHsForm = () => {
});
};
useHead({
script: [
{
src: 'https://js.hsforms.net/forms/embed/v2.js',
defer: true,
onload: renderHsForm,
},
],
});
const generatedId = computed(() => `hs-form-${unref(formId)}`);
const generatedId = computed(() => `hs-form-${unref(formId)}${props.instanceId ? `-${props.instanceId}` : ''}`);
const { theme } = useTheme();
onMounted(renderHsForm);
onUpdated(renderHsForm);
onLoaded(renderHsForm);
// @TODO: Not sure why we had these here. Safe to remove?
// onMounted(renderHsForm);
// onUpdated(renderHsForm);
watch(formId, renderHsForm);
</script>

Expand Down
1 change: 1 addition & 0 deletions components/Base/Icon.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ const filledIcons = [
'money_off',
'monitoring',
'online_prediction',
'open_in_new',
'password',
'partner_exchange',
'post_add',
Expand Down
259 changes: 259 additions & 0 deletions components/Base/Modal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
<script setup lang="ts">
import { computed, toRef } from 'vue';
import {
DialogRoot,
DialogTrigger,
DialogPortal,
DialogOverlay,
DialogContent,
DialogTitle,
DialogDescription,
DialogClose,
useForwardPropsEmits,
type DialogRootProps,
} from 'radix-vue';
import { reactivePick } from '@vueuse/core';
export interface ModalProps extends DialogRootProps {
title?: string;
description?: string;
content?: any;
overlay?: boolean;
transition?: boolean;
fullscreen?: boolean;
portal?: boolean;
close?: boolean | Record<string, any>;
preventClose?: boolean;
}
const props = withDefaults(defineProps<ModalProps>(), {
close: true,
portal: true,
overlay: true,
transition: true,
modal: true,
});
const emits = defineEmits(['update:open', 'openChange']);
const slots = defineSlots();
const rootProps = useForwardPropsEmits(reactivePick(props, 'open', 'defaultOpen', 'modal'), emits);
const contentProps = toRef(() => props.content);
const contentEvents = computed(() => {
if (props.preventClose) {
return {
pointerDownOutside: (e: Event) => e.preventDefault(),
interactOutside: (e: Event) => e.preventDefault(),
};
}
return {};
});
</script>

<template>
<DialogRoot v-slot="{ open }" v-bind="rootProps">
<DialogTrigger v-if="!!slots.default" as-child>
<slot :open="open" />
</DialogTrigger>

<DialogPortal :disabled="!portal">
<DialogOverlay v-if="overlay" :class="{ 'modal-overlay': true, 'modal-overlay--hidden': !transition }" />

<DialogContent
:class="{
modal: true,
'modal--fullscreen': fullscreen,
'modal--no-transition': !transition,
}"
v-bind="contentProps"
v-on="contentEvents"
>
<ThemeProvider variant="light">
<slot name="content">
<div
v-if="
!!slots.header || title || !!slots.title || description || !!slots.description || close || !!slots.close
"
class="modal-header"
>
<slot name="header">
<DialogTitle v-if="title || !!slots.title" class="modal-title">
<slot name="title">
{{ title }}
</slot>
</DialogTitle>

<DialogDescription v-if="description || !!slots.description" class="modal-description">
<slot name="description">
{{ description }}
</slot>
</DialogDescription>

<DialogClose as-child>
<BaseButton icon="close" aria-label="Close" color="primary" class="modal-close" />
</DialogClose>
</slot>
</div>

<div v-if="!!slots.body" class="modal-body">
<slot name="body" />
</div>

<div v-if="!!slots.footer" class="modal-footer">
<slot name="footer" />
</div>
</slot>
</ThemeProvider>
</DialogContent>
</DialogPortal>
</DialogRoot>
</template>

<style scoped lang="scss">
.modal-overlay {
position: fixed;
inset: 0;
background-color: color-mix(in srgb, #0e1c2f 90%, transparent);
z-index: 50;
}
.modal-overlay--hidden {
display: none;
}
@media (prefers-reduced-motion: no-preference) {
.modal-overlay {
transition: opacity var(--duration-150) var(--ease-out);
}
.modal-overlay[data-state='open'] {
animation: overlayShow var(--duration-150) var(--ease-in);
}
.modal-overlay[data-state='closed'] {
animation: overlayHide var(--duration-150) var(--ease-in);
}
}
.modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
min-width: var(--space-80);
max-width: 85rem;
width: 100%;
background-color: var(--background);
border-radius: var(--rounded-2xl);
box-shadow: var(--shadow-lg);
z-index: 50;
}
.modal--fullscreen {
max-width: 100vw;
max-height: 100vh;
border-radius: 0;
}
.modal--no-transition {
transition: none;
}
@media (prefers-reduced-motion: no-preference) {
.modal {
transition:
opacity var(--duration-150) var(--ease-out),
transform var(--duration-150) var(--ease-out);
}
.modal[data-state='open'] {
animation: modalShow var(--duration-150) var(--ease-out);
}
.modal[data-state='closed'] {
animation: modalHide var(--duration-150) var(--ease-in);
}
}
.modal-header {
display: flex;
padding: var(--space-6);
border-bottom: 1px solid var(--gray-200);
position: relative;
background-color: var(--background);
padding-inline-end: var(--space-12);
}
.modal-title {
margin: 0;
font-size: var(--font-size-xl);
line-height: var(--line-height-xl);
font-weight: 600;
color: var(--foreground);
}
.modal-description {
margin-block-start: var(--space-2);
font-size: var(--font-size-sm);
line-height: var(--line-height-sm);
color: var(--gray-400);
}
.modal-body {
padding: var(--space-6);
background-color: var(--background);
}
.modal-close {
position: absolute;
top: var(--space-4);
right: var(--space-4);
}
.modal-footer {
padding: var(--space-6);
border-top: 1px solid var(--gray-200);
}
@keyframes overlayShow {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes overlayHide {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@keyframes modalShow {
from {
opacity: 0;
transform: translate(-50%, -48%) scale(0.96);
}
to {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
}
@keyframes modalHide {
from {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
to {
opacity: 0;
transform: translate(-50%, -48%) scale(0.96);
}
}
</style>
Loading

0 comments on commit 96d0aa5

Please sign in to comment.