feat(fe2): project tabs synced w/ URL + various hydration mismatch fixes (#2149)

* WIP project tabs + resolved various hydration mismatches

* hydration mismatch workaround

* getting rid if old pages

* fixed page tab underlines

* minor cleanup

* support trailing slash

* a few viewer bugfixes
This commit is contained in:
Kristaps Fabians Geikins
2024-03-21 11:18:00 +01:00
committed by GitHub
parent f9f490b440
commit 32a5a36ae3
43 changed files with 504 additions and 542 deletions
@@ -8,6 +8,8 @@
:multiple="multiple"
:disabled="!items.length"
:allow-unset="allowUnset"
:label-id="labelId"
:button-id="buttonId"
by="id"
>
<template #nothing-selected>
@@ -113,6 +115,8 @@ const { result, onResult, fetchMore } = useQuery(
})
)
const buttonId = useId()
const labelId = useId()
const elementToWatchForChanges = ref(null as Nullable<HTMLElement>)
const itemContainer = ref(null as Nullable<HTMLElement>)
@@ -31,6 +31,8 @@
:rules="[isItemSelected]"
show-label
:items="applicationScopes"
:label-id="badgesLabelId"
:button-id="badgesButtonId"
by="id"
/>
<FormTextInput
@@ -95,6 +97,8 @@ const { mutate: editApplication } = useMutation(editApplicationMutation)
const { triggerNotification } = useGlobalToast()
const { handleSubmit, resetForm } = useForm<ApplicationFormValues>()
const badgesLabelId = useId()
const badgesButtonId = useId()
const name = ref('')
const scopes = ref<typeof applicationScopes.value>([])
const redirectUrl = ref('')
@@ -30,6 +30,8 @@
show-label
:items="apiTokenScopes"
mount-menu-on-body
:label-id="badgesLabelId"
:button-id="badgesButtonId"
by="id"
/>
</div>
@@ -62,6 +64,8 @@ const { handleSubmit } = useForm<TokenFormValues>()
const isOpen = defineModel<boolean>('open', { required: true })
const badgesLabelId = useId()
const badgesButtonId = useId()
const name = ref('')
const scopes = ref<typeof apiTokenScopes.value>([])
@@ -7,6 +7,8 @@
name="projectRoles"
label="Project roles"
class="min-w-[150px]"
:label-id="labelId"
:button-id="buttonId"
>
<template #nothing-selected>
{{ multiple ? 'Select roles' : 'Select role' }}
@@ -59,6 +61,8 @@ const props = defineProps<{
const elementToWatchForChanges = ref(null as Nullable<HTMLElement>)
const itemContainer = ref(null as Nullable<HTMLElement>)
const labelId = useId()
const buttonId = useId()
const { selectedValue, isArrayValue, isMultiItemArrayValue, hiddenSelectedItemCount } =
useFormSelectChildInternals<StreamRoles>({
@@ -8,6 +8,8 @@
:label="label"
:show-label="showLabel"
:name="name || 'projects'"
:label-id="labelId"
:button-id="buttonId"
by="id"
>
<template #nothing-selected>
@@ -128,6 +130,8 @@ const props = defineProps({
const elementToWatchForChanges = ref(null as Nullable<HTMLElement>)
const itemContainer = ref(null as Nullable<HTMLElement>)
const labelId = useId()
const buttonId = useId()
const { selectedValue, hiddenSelectedItemCount, isArrayValue, isMultiItemArrayValue } =
useFormSelectChildInternals<FormSelectProjects_ProjectFragment>({
@@ -8,6 +8,8 @@
label="Server roles"
class="min-w-[110px]"
:fully-control-value="fullyControlValue"
:label-id="labelId"
:button-id="buttonId"
>
<template #nothing-selected>
{{ multiple ? 'Select roles' : 'Select role' }}
@@ -67,6 +69,8 @@ const props = defineProps({
const elementToWatchForChanges = ref(null as Nullable<HTMLElement>)
const itemContainer = ref(null as Nullable<HTMLElement>)
const labelId = useId()
const buttonId = useId()
const { selectedValue, isMultiItemArrayValue, hiddenSelectedItemCount, firstItem } =
useFormSelectChildInternals<ServerRoles>({
@@ -9,6 +9,8 @@
:label="label"
:show-label="showLabel"
:name="name || 'users'"
:label-id="labelId"
:button-id="buttonId"
by="id"
>
<template #nothing-selected>
@@ -138,6 +140,8 @@ const props = defineProps({
const elementToWatchForChanges = ref(null as Nullable<HTMLElement>)
const itemContainer = ref(null as Nullable<HTMLElement>)
const labelId = useId()
const buttonId = useId()
const { selectedValue, hiddenSelectedItemCount, isArrayValue, isMultiItemArrayValue } =
useFormSelectChildInternals<FormUsersSelectItemFragment>({
@@ -1,7 +1,7 @@
<template>
<div v-if="hasNotifications">
<Menu as="div" class="flex items-center">
<MenuButton v-slot="{ open: menuOpen }" as="div">
<MenuButton :id="menuButtonId" v-slot="{ open: menuOpen }" as="div">
<div class="cursor-pointer">
<span class="sr-only">Open notifications menu</span>
<div class="relative">
@@ -50,6 +50,7 @@ import { XMarkIcon, BellIcon } from '@heroicons/vue/24/outline'
import { useActiveUser } from '~~/lib/auth/composables/activeUser'
const { activeUser } = useActiveUser()
const menuButtonId = useId()
const hasNotifications = computed(() => {
if (!activeUser.value) return false
@@ -3,7 +3,7 @@
as="div"
class="flex items-center relative sm:border-r border-outline-1 sm:pr-4"
>
<MenuButton as="div">
<MenuButton :id="menuButtonId" as="div">
<FormButton
class="hidden sm:flex"
size="sm"
@@ -101,6 +101,7 @@ const props = defineProps<{
const { copy } = useClipboard()
const copyModelLink = useCopyModelLink()
const menuButtonId = useId()
const embedDialogOpen = ref(false)
@@ -1,7 +1,7 @@
<template>
<div>
<Menu as="div" class="flex items-center">
<MenuButton v-slot="{ open: userOpen }">
<MenuButton :id="menuButtonId" v-slot="{ open: userOpen }">
<span class="sr-only">Open user menu</span>
<UserAvatar v-if="!userOpen" size="lg" :user="activeUser" hover-effect />
<UserAvatar v-else size="lg" hover-effect>
@@ -163,6 +163,7 @@ const route = useRoute()
const showInviteDialog = ref(false)
const showProfileEditDialog = ref(false)
const menuButtonId = useId()
const Icon = computed(() => (isDarkTheme.value ? SunIcon : MoonIcon))
const version = computed(() => serverInfo.value?.version)
@@ -7,6 +7,8 @@
:name="name || 'commentPermissions'"
:allow-unset="false"
:disabled="disabled"
:label-id="labelId"
:button-id="buttonId"
by="id"
>
<template #something-selected="{ value }">
@@ -41,6 +43,8 @@ const props = defineProps<{
disabled?: boolean
}>()
const labelId = useId()
const buttonId = useId()
const items = ref(commentPermissionsSelectItems)
const selectedValue = computed({
@@ -7,6 +7,8 @@
:name="name || 'visibility'"
:allow-unset="false"
:disabled="disabled"
:label-id="labelId"
:button-id="buttonId"
by="id"
>
<template #something-selected="{ value }">
@@ -44,6 +46,8 @@ const props = defineProps<{
disabled?: boolean
}>()
const labelId = useId()
const buttonId = useId()
const items = ref<
Record<
ProjectVisibility,
@@ -4,6 +4,7 @@
<h1 class="block h4 font-bold">Discussions</h1>
<div class="space-x-2 flex items-center">
<FormCheckbox
:id="checkboxId"
v-model="finalIncludeArchived"
name="includeArchived"
:value="true"
@@ -47,4 +48,6 @@ const finalIncludeArchived = computed({
get: () => props.includeArchived,
set: (newVal) => emit('update:include-archived', newVal)
})
const checkboxId = useId()
</script>
@@ -17,16 +17,9 @@
<script setup lang="ts">
import { useQuery } from '@vue/apollo-composable'
import type { Optional } from '~~/../shared/dist-esm'
import {
useGeneralProjectPageUpdateTracking,
useProjectPageItemViewType
} from '~~/lib/projects/composables/projectPages'
import { useProjectPageItemViewType } from '~~/lib/projects/composables/projectPages'
import { projectDiscussionsPageQuery } from '~~/lib/projects/graphql/queries'
definePageMeta({
middleware: ['require-valid-project']
})
const gridOrList = useProjectPageItemViewType('Discussions')
const includeArchived = ref(undefined as Optional<true>)
@@ -35,7 +28,6 @@ const projectId = computed(() => route.params.id as string)
const { result } = useQuery(projectDiscussionsPageQuery, () => ({
projectId: projectId.value
}))
useGeneralProjectPageUpdateTracking({ projectId })
const project = computed(() => result.value?.project)
</script>
@@ -3,6 +3,7 @@
<div>
<LayoutMenu
v-model:open="showActionsMenu"
:menu-id="menuId"
:items="actionsItems"
@click.stop.prevent
@chosen="onActionChosen"
@@ -89,6 +90,7 @@ const props = defineProps<{
const copyModelLink = useCopyModelLink()
const { copy } = useClipboard()
const menuId = useId()
const showActionsMenu = ref(false)
const openDialog = ref(null as Nullable<ActionTypes>)
@@ -62,6 +62,8 @@
class="grow shrink sm:w-[120px] md:w-44"
clearable
fixed-height
:label-id="sourceAppsLabelId"
:button-id="sourceAppsBtnId"
/>
<LayoutGridListToggle v-model="finalGridOrList" class="shrink-0" />
</div>
@@ -135,6 +137,8 @@ const props = defineProps<{
}>()
const localSearch = ref('')
const sourceAppsLabelId = useId()
const sourceAppsBtnId = useId()
const mp = useMixpanel()
const trackFederateAll = () =>
@@ -28,14 +28,7 @@ import { useQuery } from '@vue/apollo-composable'
import type { SourceAppDefinition } from '@speckle/shared'
import type { FormUsersSelectItemFragment } from '~~/lib/common/generated/gql/graphql'
import { projectModelsPageQuery } from '~~/lib/projects/graphql/queries'
import {
useGeneralProjectPageUpdateTracking,
useProjectPageItemViewType
} from '~~/lib/projects/composables/projectPages'
definePageMeta({
middleware: ['require-valid-project']
})
import { useProjectPageItemViewType } from '~~/lib/projects/composables/projectPages'
const route = useRoute()
const projectId = computed(() => route.params.id as string)
@@ -51,7 +44,6 @@ const { result } = useQuery(projectModelsPageQuery, () => ({
}))
const project = computed(() => result.value?.project)
useGeneralProjectPageUpdateTracking({ projectId })
const clearSearch = () => {
search.value = ''
@@ -8,6 +8,8 @@
:name="name || 'role'"
:allow-unset="false"
:disabled="disabled"
:label-id="labelId"
:button-id="buttonId"
hide-checkmarks
by="id"
class="min-w-[85px]"
@@ -54,6 +56,8 @@ const props = defineProps<{
hideOwner?: boolean
}>()
const labelId = useId()
const buttonId = useId()
const items = ref(
reduce(
roleSelectItems,
@@ -49,6 +49,8 @@
:rules="[isItemSelected]"
show-label
:items="webhookTriggerItems"
:label-id="badgesLabelId"
:button-id="badgesButtonId"
by="id"
/>
</div>
@@ -100,6 +102,8 @@ const triggers = ref<typeof webhookTriggerItems.value>([])
const url = ref('')
const description = ref('')
const secret = ref('')
const badgesLabelId = useId()
const badgesButtonId = useId()
const webhookTriggerItems = computed(() => {
return Object.values(WebhookTriggers).map((value) => ({
@@ -1,40 +1,42 @@
<template>
<Popover as="div" class="relative z-30">
<PopoverButton v-slot="{ open }" as="template">
<ViewerControlsButtonToggle flat secondary :active="open || isActive">
<!-- <ChevronUpDownIcon class="w-5 h-5 rotate-45" /> -->
<IconExplode class="h-4 w-4 sm:h-5 sm:w-5" />
</ViewerControlsButtonToggle>
</PopoverButton>
<Transition
enter-active-class="transform ease-out duration-300 transition"
enter-from-class="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2"
enter-to-class="translate-y-0 opacity-100 sm:translate-x-0"
leave-active-class="transition ease-in duration-100"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<PopoverPanel
class="absolute translate-x-0 left-12 top-0 p-2 bg-foundation max-h-64 simple-scrollbar overflow-y-auto outline outline-2 outline-primary-muted rounded-lg shadow-lg overflow-hidden flex flex-col space-y-2"
<div class="relative z-30">
<Popover as="div" class="relative z-30">
<PopoverButton v-slot="{ open }" as="template">
<ViewerControlsButtonToggle flat secondary :active="open || isActive">
<!-- <ChevronUpDownIcon class="w-5 h-5 rotate-45" /> -->
<IconExplode class="h-4 w-4 sm:h-5 sm:w-5" />
</ViewerControlsButtonToggle>
</PopoverButton>
<Transition
enter-active-class="transform ease-out duration-300 transition"
enter-from-class="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2"
enter-to-class="translate-y-0 opacity-100 sm:translate-x-0"
leave-active-class="transition ease-in duration-100"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<div class="flex items-center space-x-1">
<input
id="intensity"
v-model="explodeFactor"
class="w-24 sm:w-32 h-2 mr-2"
type="range"
name="intensity"
min="0"
max="1"
step="0.01"
/>
<label class="text-xs sm:text-sm text-foreground-2" for="intensity">
Intensity
</label>
</div>
</PopoverPanel>
</Transition>
</Popover>
<PopoverPanel
class="absolute translate-x-0 left-12 top-0 p-2 bg-foundation max-h-64 simple-scrollbar overflow-y-auto outline outline-2 outline-primary-muted rounded-lg shadow-lg overflow-hidden flex flex-col space-y-2"
>
<div class="flex items-center space-x-1">
<input
id="intensity"
v-model="explodeFactor"
class="w-24 sm:w-32 h-2 mr-2"
type="range"
name="intensity"
min="0"
max="1"
step="0.01"
/>
<label class="text-xs sm:text-sm text-foreground-2" for="intensity">
Intensity
</label>
</div>
</PopoverPanel>
</Transition>
</Popover>
</div>
</template>
<script setup lang="ts">
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
@@ -6,6 +6,8 @@
:show-label="false"
:name="name || 'units'"
:allow-unset="false"
:label-id="labelId"
:button-id="buttonId"
>
<template #something-selected>
<div>{{ fullUnitName }}</div>
@@ -32,6 +34,9 @@ const props = defineProps<{
name?: string
}>()
const labelId = useId()
const buttonId = useId()
// Use a ref for unitDisplayNames
const unitDisplayNames = ref<UnitDisplayNames>({
mm: 'Millimeters',
@@ -1,90 +1,92 @@
<template>
<Popover as="div" class="relative z-30">
<PopoverButton v-slot="{ open }" as="template">
<ViewerControlsButtonToggle flat secondary :active="open">
<SunIcon class="w-5 h-5" />
</ViewerControlsButtonToggle>
</PopoverButton>
<Transition
enter-active-class="transform ease-out duration-300 transition"
enter-from-class="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2"
enter-to-class="translate-y-0 opacity-100 sm:translate-x-0"
leave-active-class="transition ease-in duration-100"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<PopoverPanel
class="absolute translate-x-0 left-10 sm:left-12 top-2 bg-foundation max-h-64 simple-scrollbar overflow-y-auto outline outline-2 outline-primary-muted rounded-lg shadow-lg overflow-hidden flex flex-col space-y-2"
<div class="relative z-30">
<Popover as="div" class="relative z-30">
<PopoverButton v-slot="{ open }" as="template">
<ViewerControlsButtonToggle flat secondary :active="open">
<SunIcon class="w-5 h-5" />
</ViewerControlsButtonToggle>
</PopoverButton>
<Transition
enter-active-class="transform ease-out duration-300 transition"
enter-from-class="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2"
enter-to-class="translate-y-0 opacity-100 sm:translate-x-0"
leave-active-class="transition ease-in duration-100"
leave-from-class="opacity-100"
leave-to-class="opacity-0"
>
<div class="p-2 border-b border-outline flex gap-2 items-center">
<div class="scale-90">
<FormSwitch v-model="sunlightShadows" />
<PopoverPanel
class="absolute translate-x-0 left-10 sm:left-12 top-2 bg-foundation max-h-64 simple-scrollbar overflow-y-auto outline outline-2 outline-primary-muted rounded-lg shadow-lg overflow-hidden flex flex-col space-y-2"
>
<div class="p-2 border-b border-outline flex gap-2 items-center">
<div class="scale-90">
<FormSwitch v-model="sunlightShadows" />
</div>
<span class="text-foreground text-sm">Sun Shadows</span>
</div>
<span class="text-foreground text-sm">Sun Shadows</span>
</div>
<div class="flex items-center gap-1 px-2">
<input
id="intensity"
v-model="intensity"
class="w-24 sm:w-32 h-2 mr-2"
type="range"
name="intensity"
min="1"
max="10"
step="0.05"
/>
<label class="text-xs sm:text-sm text-foreground-2" for="intensity">
Intensity
</label>
</div>
<div class="flex items-center gap-1 px-2">
<input
id="elevation"
v-model="elevation"
class="w-24 sm:w-32 h-2 mr-2"
type="range"
name="elevation"
min="0"
:max="Math.PI"
step="0.05"
/>
<label class="text-xs sm:text-sm text-foreground-2" for="elevation">
Elevation
</label>
</div>
<div class="flex items-center gap-1 px-2">
<input
id="azimuth"
v-model="azimuth"
class="w-24 sm:w-32 h-2 mr-2"
type="range"
name="azimuth"
:min="-Math.PI * 0.5"
:max="Math.PI * 0.5"
step="0.05"
/>
<label class="text-xs sm:text-sm text-foreground-2" for="azimuth">
Azimuth
</label>
</div>
<div class="flex items-center gap-1 px-2 pb-2">
<input
id="indirect"
v-model="indirectLightIntensity"
class="w-24 sm:w-32 h-2 mr-2"
type="range"
name="indirect"
min="0"
max="5"
step="0.05"
/>
<label class="text-xs sm:text-sm text-foreground-2" for="indirect">
Indirect
</label>
</div>
</PopoverPanel>
</Transition>
</Popover>
<div class="flex items-center gap-1 px-2">
<input
id="intensity"
v-model="intensity"
class="w-24 sm:w-32 h-2 mr-2"
type="range"
name="intensity"
min="1"
max="10"
step="0.05"
/>
<label class="text-xs sm:text-sm text-foreground-2" for="intensity">
Intensity
</label>
</div>
<div class="flex items-center gap-1 px-2">
<input
id="elevation"
v-model="elevation"
class="w-24 sm:w-32 h-2 mr-2"
type="range"
name="elevation"
min="0"
:max="Math.PI"
step="0.05"
/>
<label class="text-xs sm:text-sm text-foreground-2" for="elevation">
Elevation
</label>
</div>
<div class="flex items-center gap-1 px-2">
<input
id="azimuth"
v-model="azimuth"
class="w-24 sm:w-32 h-2 mr-2"
type="range"
name="azimuth"
:min="-Math.PI * 0.5"
:max="Math.PI * 0.5"
step="0.05"
/>
<label class="text-xs sm:text-sm text-foreground-2" for="azimuth">
Azimuth
</label>
</div>
<div class="flex items-center gap-1 px-2 pb-2">
<input
id="indirect"
v-model="indirectLightIntensity"
class="w-24 sm:w-32 h-2 mr-2"
type="range"
name="indirect"
min="0"
max="5"
step="0.05"
/>
<label class="text-xs sm:text-sm text-foreground-2" for="indirect">
Indirect
</label>
</div>
</PopoverPanel>
</Transition>
</Popover>
</div>
</template>
<script setup lang="ts">
import { Popover, PopoverButton, PopoverPanel } from '@headlessui/vue'
@@ -1,6 +1,6 @@
<template>
<Menu as="div" class="relative z-30">
<MenuButton v-slot="{ open }" as="template">
<MenuButton :id="menuButtonId" v-slot="{ open }" as="template">
<ViewerControlsButtonToggle flat secondary :active="open">
<IconViews class="w-5 h-5" />
</ViewerControlsButtonToggle>
@@ -74,6 +74,7 @@ const {
} = useInjectedViewerState()
const { setView: setViewRaw } = useCameraUtilities()
const mp = useMixpanel()
const menuButtonId = useId()
const setView = (v: CanonicalView | SpeckleView) => {
setViewRaw(v)
@@ -124,10 +124,10 @@ const documents = {
"\n query ProjectLatestCommentThreads(\n $projectId: String!\n $cursor: String = null\n $filter: ProjectCommentsFilter = null\n ) {\n project(id: $projectId) {\n id\n commentThreads(cursor: $cursor, limit: 8, filter: $filter) {\n totalCount\n cursor\n items {\n ...ProjectPageLatestItemsCommentItem\n }\n }\n }\n }\n": types.ProjectLatestCommentThreadsDocument,
"\n query ProjectInvite($projectId: String!, $token: String) {\n projectInvite(projectId: $projectId, token: $token) {\n ...ProjectsInviteBanner\n }\n }\n": types.ProjectInviteDocument,
"\n query ProjectModelCheck($projectId: String!, $modelId: String!) {\n project(id: $projectId) {\n model(id: $modelId) {\n id\n }\n }\n }\n": types.ProjectModelCheckDocument,
"\n query ProjectModelPage(\n $projectId: String!\n $modelId: String!\n $versionsCursor: String\n ) {\n project(id: $projectId) {\n ...ProjectModelPageHeaderProject\n ...ProjectModelPageVersionsProject\n }\n }\n": types.ProjectModelPageDocument,
"\n query ProjectModelVersions(\n $projectId: String!\n $modelId: String!\n $versionsCursor: String\n ) {\n project(id: $projectId) {\n ...ProjectModelPageVersionsPagination\n }\n }\n": types.ProjectModelVersionsDocument,
"\n query ProjectModelsPage($projectId: String!) {\n project(id: $projectId) {\n ...ProjectModelsPageHeader_Project\n ...ProjectModelsPageResults_Project\n }\n }\n": types.ProjectModelsPageDocument,
"\n query ProjectDiscussionsPage($projectId: String!) {\n project(id: $projectId) {\n ...ProjectDiscussionsPageHeader_Project\n ...ProjectDiscussionsPageResults_Project\n }\n }\n": types.ProjectDiscussionsPageDocument,
"\n query ProjectModelPage(\n $projectId: String!\n $modelId: String!\n $versionsCursor: String\n ) {\n project(id: $projectId) {\n id\n ...ProjectModelPageHeaderProject\n ...ProjectModelPageVersionsProject\n }\n }\n": types.ProjectModelPageDocument,
"\n query ProjectModelVersions(\n $projectId: String!\n $modelId: String!\n $versionsCursor: String\n ) {\n project(id: $projectId) {\n id\n ...ProjectModelPageVersionsPagination\n }\n }\n": types.ProjectModelVersionsDocument,
"\n query ProjectModelsPage($projectId: String!) {\n project(id: $projectId) {\n id\n ...ProjectModelsPageHeader_Project\n ...ProjectModelsPageResults_Project\n }\n }\n": types.ProjectModelsPageDocument,
"\n query ProjectDiscussionsPage($projectId: String!) {\n project(id: $projectId) {\n id\n ...ProjectDiscussionsPageHeader_Project\n ...ProjectDiscussionsPageResults_Project\n }\n }\n": types.ProjectDiscussionsPageDocument,
"\n query ProjectWebhooks($projectId: String!) {\n project(id: $projectId) {\n id\n name\n webhooks {\n items {\n streamId\n triggers\n enabled\n url\n id\n description\n history(limit: 5) {\n items {\n status\n statusInfo\n }\n }\n }\n totalCount\n }\n }\n }\n": types.ProjectWebhooksDocument,
"\n query Blob($blobId: String!, $streamId: String!) {\n stream(id: $streamId) {\n blob(id: $blobId) {\n id\n fileName\n fileType\n fileSize\n createdAt\n }\n }\n }\n": types.BlobDocument,
"\n subscription OnProjectUpdated($id: String!) {\n projectUpdated(id: $id) {\n id\n type\n project {\n ...ProjectPageProject\n ...ProjectDashboardItemNoModels\n }\n }\n }\n": types.OnProjectUpdatedDocument,
@@ -638,19 +638,19 @@ export function graphql(source: "\n query ProjectModelCheck($projectId: String!
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query ProjectModelPage(\n $projectId: String!\n $modelId: String!\n $versionsCursor: String\n ) {\n project(id: $projectId) {\n ...ProjectModelPageHeaderProject\n ...ProjectModelPageVersionsProject\n }\n }\n"): (typeof documents)["\n query ProjectModelPage(\n $projectId: String!\n $modelId: String!\n $versionsCursor: String\n ) {\n project(id: $projectId) {\n ...ProjectModelPageHeaderProject\n ...ProjectModelPageVersionsProject\n }\n }\n"];
export function graphql(source: "\n query ProjectModelPage(\n $projectId: String!\n $modelId: String!\n $versionsCursor: String\n ) {\n project(id: $projectId) {\n id\n ...ProjectModelPageHeaderProject\n ...ProjectModelPageVersionsProject\n }\n }\n"): (typeof documents)["\n query ProjectModelPage(\n $projectId: String!\n $modelId: String!\n $versionsCursor: String\n ) {\n project(id: $projectId) {\n id\n ...ProjectModelPageHeaderProject\n ...ProjectModelPageVersionsProject\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query ProjectModelVersions(\n $projectId: String!\n $modelId: String!\n $versionsCursor: String\n ) {\n project(id: $projectId) {\n ...ProjectModelPageVersionsPagination\n }\n }\n"): (typeof documents)["\n query ProjectModelVersions(\n $projectId: String!\n $modelId: String!\n $versionsCursor: String\n ) {\n project(id: $projectId) {\n ...ProjectModelPageVersionsPagination\n }\n }\n"];
export function graphql(source: "\n query ProjectModelVersions(\n $projectId: String!\n $modelId: String!\n $versionsCursor: String\n ) {\n project(id: $projectId) {\n id\n ...ProjectModelPageVersionsPagination\n }\n }\n"): (typeof documents)["\n query ProjectModelVersions(\n $projectId: String!\n $modelId: String!\n $versionsCursor: String\n ) {\n project(id: $projectId) {\n id\n ...ProjectModelPageVersionsPagination\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query ProjectModelsPage($projectId: String!) {\n project(id: $projectId) {\n ...ProjectModelsPageHeader_Project\n ...ProjectModelsPageResults_Project\n }\n }\n"): (typeof documents)["\n query ProjectModelsPage($projectId: String!) {\n project(id: $projectId) {\n ...ProjectModelsPageHeader_Project\n ...ProjectModelsPageResults_Project\n }\n }\n"];
export function graphql(source: "\n query ProjectModelsPage($projectId: String!) {\n project(id: $projectId) {\n id\n ...ProjectModelsPageHeader_Project\n ...ProjectModelsPageResults_Project\n }\n }\n"): (typeof documents)["\n query ProjectModelsPage($projectId: String!) {\n project(id: $projectId) {\n id\n ...ProjectModelsPageHeader_Project\n ...ProjectModelsPageResults_Project\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n query ProjectDiscussionsPage($projectId: String!) {\n project(id: $projectId) {\n ...ProjectDiscussionsPageHeader_Project\n ...ProjectDiscussionsPageResults_Project\n }\n }\n"): (typeof documents)["\n query ProjectDiscussionsPage($projectId: String!) {\n project(id: $projectId) {\n ...ProjectDiscussionsPageHeader_Project\n ...ProjectDiscussionsPageResults_Project\n }\n }\n"];
export function graphql(source: "\n query ProjectDiscussionsPage($projectId: String!) {\n project(id: $projectId) {\n id\n ...ProjectDiscussionsPageHeader_Project\n ...ProjectDiscussionsPageResults_Project\n }\n }\n"): (typeof documents)["\n query ProjectDiscussionsPage($projectId: String!) {\n project(id: $projectId) {\n id\n ...ProjectDiscussionsPageHeader_Project\n ...ProjectDiscussionsPageResults_Project\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -3885,10 +3885,10 @@ export const ProjectModelChildrenTreeDocument = {"kind":"Document","definitions"
export const ProjectLatestCommentThreadsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectLatestCommentThreads"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}},"defaultValue":{"kind":"NullValue"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCommentsFilter"}},"defaultValue":{"kind":"NullValue"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"8"}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsCommentItem"}}]}}]}}]}}]}},...ProjectPageLatestItemsCommentItemFragmentDoc.definitions,...FormUsersSelectItemFragmentDoc.definitions]} as unknown as DocumentNode<ProjectLatestCommentThreadsQuery, ProjectLatestCommentThreadsQueryVariables>;
export const ProjectInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectInvite"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"projectId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}},{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsInviteBanner"}}]}}]}},...ProjectsInviteBannerFragmentDoc.definitions,...LimitedUserAvatarFragmentDoc.definitions]} as unknown as DocumentNode<ProjectInviteQuery, ProjectInviteQueryVariables>;
export const ProjectModelCheckDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectModelCheck"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"model"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode<ProjectModelCheckQuery, ProjectModelCheckQueryVariables>;
export const ProjectModelPageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectModelPage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"versionsCursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelPageHeaderProject"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelPageVersionsProject"}}]}}]}},...ProjectModelPageHeaderProjectFragmentDoc.definitions,...ProjectModelPageVersionsProjectFragmentDoc.definitions,...ProjectPageProjectHeaderFragmentDoc.definitions,...PendingFileUploadFragmentDoc.definitions,...ProjectModelPageVersionsPaginationFragmentDoc.definitions,...ProjectModelPageVersionsCardVersionFragmentDoc.definitions,...LimitedUserAvatarFragmentDoc.definitions,...ProjectModelPageDialogDeleteVersionFragmentDoc.definitions,...ProjectModelPageDialogMoveToVersionFragmentDoc.definitions,...ModelCardAutomationStatus_VersionFragmentDoc.definitions,...ModelCardAutomationStatus_AutomationsStatusFragmentDoc.definitions,...ProjectsModelPageEmbed_ProjectFragmentDoc.definitions,...ProjectsPageTeamDialogManagePermissions_ProjectFragmentDoc.definitions]} as unknown as DocumentNode<ProjectModelPageQuery, ProjectModelPageQueryVariables>;
export const ProjectModelVersionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectModelVersions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"versionsCursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelPageVersionsPagination"}}]}}]}},...ProjectModelPageVersionsPaginationFragmentDoc.definitions,...ProjectModelPageVersionsCardVersionFragmentDoc.definitions,...LimitedUserAvatarFragmentDoc.definitions,...ProjectModelPageDialogDeleteVersionFragmentDoc.definitions,...ProjectModelPageDialogMoveToVersionFragmentDoc.definitions,...ModelCardAutomationStatus_VersionFragmentDoc.definitions,...ModelCardAutomationStatus_AutomationsStatusFragmentDoc.definitions,...ProjectsModelPageEmbed_ProjectFragmentDoc.definitions,...ProjectsPageTeamDialogManagePermissions_ProjectFragmentDoc.definitions]} as unknown as DocumentNode<ProjectModelVersionsQuery, ProjectModelVersionsQueryVariables>;
export const ProjectModelsPageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectModelsPage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelsPageHeader_Project"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelsPageResults_Project"}}]}}]}},...ProjectModelsPageHeader_ProjectFragmentDoc.definitions,...FormUsersSelectItemFragmentDoc.definitions,...ProjectModelsPageResults_ProjectFragmentDoc.definitions,...ProjectPageLatestItemsModelsFragmentDoc.definitions,...ProjectPageModelsStructureItem_ProjectFragmentDoc.definitions,...ProjectPageModelsActions_ProjectFragmentDoc.definitions,...ProjectsModelPageEmbed_ProjectFragmentDoc.definitions,...ProjectsPageTeamDialogManagePermissions_ProjectFragmentDoc.definitions]} as unknown as DocumentNode<ProjectModelsPageQuery, ProjectModelsPageQueryVariables>;
export const ProjectDiscussionsPageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectDiscussionsPage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDiscussionsPageHeader_Project"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDiscussionsPageResults_Project"}}]}}]}},...ProjectDiscussionsPageHeader_ProjectFragmentDoc.definitions,...ProjectDiscussionsPageResults_ProjectFragmentDoc.definitions]} as unknown as DocumentNode<ProjectDiscussionsPageQuery, ProjectDiscussionsPageQueryVariables>;
export const ProjectModelPageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectModelPage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"versionsCursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelPageHeaderProject"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelPageVersionsProject"}}]}}]}},...ProjectModelPageHeaderProjectFragmentDoc.definitions,...ProjectModelPageVersionsProjectFragmentDoc.definitions,...ProjectPageProjectHeaderFragmentDoc.definitions,...PendingFileUploadFragmentDoc.definitions,...ProjectModelPageVersionsPaginationFragmentDoc.definitions,...ProjectModelPageVersionsCardVersionFragmentDoc.definitions,...LimitedUserAvatarFragmentDoc.definitions,...ProjectModelPageDialogDeleteVersionFragmentDoc.definitions,...ProjectModelPageDialogMoveToVersionFragmentDoc.definitions,...ModelCardAutomationStatus_VersionFragmentDoc.definitions,...ModelCardAutomationStatus_AutomationsStatusFragmentDoc.definitions,...ProjectsModelPageEmbed_ProjectFragmentDoc.definitions,...ProjectsPageTeamDialogManagePermissions_ProjectFragmentDoc.definitions]} as unknown as DocumentNode<ProjectModelPageQuery, ProjectModelPageQueryVariables>;
export const ProjectModelVersionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectModelVersions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"modelId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"versionsCursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelPageVersionsPagination"}}]}}]}},...ProjectModelPageVersionsPaginationFragmentDoc.definitions,...ProjectModelPageVersionsCardVersionFragmentDoc.definitions,...LimitedUserAvatarFragmentDoc.definitions,...ProjectModelPageDialogDeleteVersionFragmentDoc.definitions,...ProjectModelPageDialogMoveToVersionFragmentDoc.definitions,...ModelCardAutomationStatus_VersionFragmentDoc.definitions,...ModelCardAutomationStatus_AutomationsStatusFragmentDoc.definitions,...ProjectsModelPageEmbed_ProjectFragmentDoc.definitions,...ProjectsPageTeamDialogManagePermissions_ProjectFragmentDoc.definitions]} as unknown as DocumentNode<ProjectModelVersionsQuery, ProjectModelVersionsQueryVariables>;
export const ProjectModelsPageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectModelsPage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelsPageHeader_Project"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectModelsPageResults_Project"}}]}}]}},...ProjectModelsPageHeader_ProjectFragmentDoc.definitions,...FormUsersSelectItemFragmentDoc.definitions,...ProjectModelsPageResults_ProjectFragmentDoc.definitions,...ProjectPageLatestItemsModelsFragmentDoc.definitions,...ProjectPageModelsStructureItem_ProjectFragmentDoc.definitions,...ProjectPageModelsActions_ProjectFragmentDoc.definitions,...ProjectsModelPageEmbed_ProjectFragmentDoc.definitions,...ProjectsPageTeamDialogManagePermissions_ProjectFragmentDoc.definitions]} as unknown as DocumentNode<ProjectModelsPageQuery, ProjectModelsPageQueryVariables>;
export const ProjectDiscussionsPageDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectDiscussionsPage"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDiscussionsPageHeader_Project"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDiscussionsPageResults_Project"}}]}}]}},...ProjectDiscussionsPageHeader_ProjectFragmentDoc.definitions,...ProjectDiscussionsPageResults_ProjectFragmentDoc.definitions]} as unknown as DocumentNode<ProjectDiscussionsPageQuery, ProjectDiscussionsPageQueryVariables>;
export const ProjectWebhooksDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectWebhooks"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"webhooks"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"streamId"}},{"kind":"Field","name":{"kind":"Name","value":"triggers"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"history"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"5"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"statusInfo"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]}}]} as unknown as DocumentNode<ProjectWebhooksQuery, ProjectWebhooksQueryVariables>;
export const BlobDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Blob"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"blobId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"stream"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"blob"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"blobId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"fileName"}},{"kind":"Field","name":{"kind":"Name","value":"fileType"}},{"kind":"Field","name":{"kind":"Name","value":"fileSize"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]}}]} as unknown as DocumentNode<BlobQuery, BlobQueryVariables>;
export const OnProjectUpdatedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"OnProjectUpdated"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectUpdated"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"project"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageProject"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"}}]}}]}}]}},...ProjectPageProjectFragmentDoc.definitions,...ProjectPageProjectHeaderFragmentDoc.definitions,...ProjectPageStatsBlockTeamFragmentDoc.definitions,...LimitedUserAvatarFragmentDoc.definitions,...ProjectPageTeamDialogFragmentDoc.definitions,...ProjectsPageTeamDialogManagePermissions_ProjectFragmentDoc.definitions,...ProjectPageStatsBlockVersionsFragmentDoc.definitions,...ProjectPageStatsBlockModelsFragmentDoc.definitions,...ProjectPageStatsBlockCommentsFragmentDoc.definitions,...ProjectPageLatestItemsModelsFragmentDoc.definitions,...ProjectPageModelsStructureItem_ProjectFragmentDoc.definitions,...ProjectPageModelsActions_ProjectFragmentDoc.definitions,...ProjectsModelPageEmbed_ProjectFragmentDoc.definitions,...ProjectPageLatestItemsCommentsFragmentDoc.definitions,...ProjectDashboardItemNoModelsFragmentDoc.definitions,...ProjectPageModelsCardProjectFragmentDoc.definitions]} as unknown as DocumentNode<OnProjectUpdatedSubscription, OnProjectUpdatedSubscriptionVariables>;
@@ -11,7 +11,17 @@ export const forgottenPasswordRoute = '/authn/forgotten-password'
export const onboardingRoute = '/onboarding'
export const downloadManagerRoute = '/download-manager'
export const serverManagementRoute = '/server-management'
export const projectRoute = (id: string) => `/projects/${id}`
export const projectRoute = (
id: string,
tab?: 'models' | 'discussions' | 'automations'
) => {
let res = `/projects/${id}`
if (tab && tab !== 'models') {
res += `/${tab}`
}
return res
}
export const modelRoute = (
projectId: string,
resourceIdString: string,
@@ -167,6 +167,7 @@ export const projectModelPageQuery = graphql(`
$versionsCursor: String
) {
project(id: $projectId) {
id
...ProjectModelPageHeaderProject
...ProjectModelPageVersionsProject
}
@@ -180,6 +181,7 @@ export const projectModelVersionsQuery = graphql(`
$versionsCursor: String
) {
project(id: $projectId) {
id
...ProjectModelPageVersionsPagination
}
}
@@ -188,6 +190,7 @@ export const projectModelVersionsQuery = graphql(`
export const projectModelsPageQuery = graphql(`
query ProjectModelsPage($projectId: String!) {
project(id: $projectId) {
id
...ProjectModelsPageHeader_Project
...ProjectModelsPageResults_Project
}
@@ -197,6 +200,7 @@ export const projectModelsPageQuery = graphql(`
export const projectDiscussionsPageQuery = graphql(`
query ProjectDiscussionsPage($projectId: String!) {
project(id: $projectId) {
id
...ProjectDiscussionsPageHeader_Project
...ProjectDiscussionsPageResults_Project
}
@@ -998,10 +998,15 @@ export function useSetupViewer(params: UseSetupViewerParams): InjectableViewerSt
const stateWithResources = setupResourceResponse(initialStateWithRequest)
const state: InjectableViewerState = setupInterfaceState(stateWithResources)
// Inject it into descendant components
provide(InjectableViewerStateKey, state)
// We don't want the state to ever be proxified (e.g. when passed through props),
// cause that will break composables (refs will be automatically unwrapped as if
// they're accessed in a template)
const rawState = markRaw(state)
return state
// Inject it into descendant components
provide(InjectableViewerStateKey, rawState)
return rawState
}
/**
@@ -1,59 +0,0 @@
<template>
<div>
<div>
<Portal to="navigation">
<HeaderNavLink
v-if="project"
:to="projectRoute(project.id)"
:name="project.name"
></HeaderNavLink>
</Portal>
</div>
<div v-if="project">
<ProjectPageDiscussionsHeader
v-model:grid-or-list="gridOrList"
v-model:include-archived="includeArchived"
:project="project"
/>
<ProjectPageDiscussionsResults
:grid-or-list="gridOrList"
:project="project"
:include-archived="!!includeArchived"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { useQuery } from '@vue/apollo-composable'
import type { Optional } from '~~/../shared/dist-esm'
import { projectRoute } from '~~/lib/common/helpers/route'
import {
useGeneralProjectPageUpdateTracking,
useProjectPageItemViewType
} from '~~/lib/projects/composables/projectPages'
import { projectDiscussionsPageQuery } from '~~/lib/projects/graphql/queries'
definePageMeta({
middleware: ['require-valid-project']
})
const gridOrList = useProjectPageItemViewType('Discussions')
const includeArchived = ref(undefined as Optional<true>)
const route = useRoute()
const projectId = computed(() => route.params.id as string)
const { result } = useQuery(projectDiscussionsPageQuery, () => ({
projectId: projectId.value
}))
useGeneralProjectPageUpdateTracking({ projectId })
const project = computed(() => result.value?.project)
const title = computed(() =>
project.value?.name.length ? `Discussions - ${project.value.name}` : ''
)
useHead({
title
})
</script>
@@ -1,6 +1,6 @@
<template>
<div>
<template v-if="project">
<div v-if="project">
<ProjectsInviteBanner
:invite="invite"
:show-stream-name="false"
@@ -16,23 +16,26 @@
class="w-full md:w-72 shrink-0"
/>
</div>
</template>
<!-- No v-if=project to ensure internal queries trigger ASAP -->
<LayoutPageTabs v-if="project" v-slot="{ activeItem }" :items="pageTabItems">
<ProjectPageModelsTab v-if="activeItem.id === 'models'" />
<ProjectPageDiscussionsTab v-if="activeItem.id === 'discussions'" />
<!-- <ProjectPageAutomationsTab v-if="activeItem.id === 'automations'" /> -->
</LayoutPageTabs>
<LayoutPageTabs v-model:active-item="activePageTab" :items="pageTabItems">
<NuxtPage />
</LayoutPageTabs>
</div>
</div>
</template>
<script setup lang="ts">
import { useQuery } from '@vue/apollo-composable'
import { useApolloClient, useQuery } from '@vue/apollo-composable'
import type { Optional } from '@speckle/shared'
import { graphql } from '~~/lib/common/generated/gql'
import { projectPageQuery } from '~~/lib/projects/graphql/queries'
import {
projectDiscussionsPageQuery,
projectModelsPageQuery,
projectPageQuery
} from '~~/lib/projects/graphql/queries'
import { useGeneralProjectPageUpdateTracking } from '~~/lib/projects/composables/projectPages'
import { LayoutPageTabs, type LayoutPageTabItem } from '@speckle/ui-components'
import { CubeIcon, ChatBubbleLeftRightIcon } from '@heroicons/vue/24/outline'
import { projectRoute } from '~/lib/common/helpers/route'
import { convertThrowIntoFetchResult } from '~/lib/common/helpers/graphql'
graphql(`
fragment ProjectPageProject on Project {
@@ -50,7 +53,17 @@ graphql(`
`)
definePageMeta({
middleware: ['require-valid-project']
middleware: [
'require-valid-project',
function (to) {
// Redirect from /projects/:id/models to /projects/:id
const projectId = to.params.id as string
if (/\/models\/?$/i.test(to.path)) {
return navigateTo(projectRoute(projectId))
}
}
],
alias: '/projects/:id/models'
})
const route = useRoute()
@@ -118,4 +131,59 @@ const pageTabItems = computed((): LayoutPageTabItem[] => [
// tag: 'New'
// }
])
const activePageTab = computed({
get: () => {
const path = router.currentRoute.value.path
if (/\/discussions\/?$/i.test(path)) return pageTabItems.value[1]
if (/\/automations\/?$/i.test(path)) return pageTabItems.value[2]
return pageTabItems.value[0]
},
set: (val: LayoutPageTabItem) => {
switch (val.id) {
case 'models':
router.push({ path: projectRoute(projectId.value, 'models') })
break
case 'discussions':
router.push({ path: projectRoute(projectId.value, 'discussions') })
break
case 'automations':
router.push({ path: projectRoute(projectId.value, 'automations') })
break
}
}
})
if (process.server) {
/**
* There seems to be some sort of vue/nuxt bug where Apollo queries in tabs cause
* weird hydration mismatches. Honestly I've no idea wtf is happening, but if we preload
* those queries from the root page it seems to work. This is a hack, but it works.
*
* Hopefully we can figure this out at some point, cause this is quite nasty
*/
const serverActiveTab = activePageTab.value
const client = useApolloClient().client
if (serverActiveTab.id === 'models') {
await client
.query({
query: projectModelsPageQuery,
variables: {
projectId: projectId.value
}
})
.catch(convertThrowIntoFetchResult)
} else if (serverActiveTab.id === 'discussions') {
await client
.query({
query: projectDiscussionsPageQuery,
variables: {
projectId: projectId.value
}
})
.catch(convertThrowIntoFetchResult)
}
}
</script>
@@ -0,0 +1,3 @@
<template>
<ProjectPageDiscussionsTab />
</template>
@@ -0,0 +1,5 @@
<template>
<div>
<ProjectPageModelsTab />
</div>
</template>
@@ -1,80 +0,0 @@
<template>
<div>
<div>
<Portal to="navigation">
<HeaderNavLink
v-if="project"
:to="projectRoute(project.id)"
:name="project.name"
></HeaderNavLink>
</Portal>
</div>
<div v-if="project">
<ProjectPageModelsHeader
v-model:selected-apps="selectedApps"
v-model:selected-members="selectedMembers"
v-model:grid-or-list="gridOrList"
v-model:search="search"
:project="project"
:disabled="loading"
class="z-[1] relative"
/>
<ProjectPageModelsResults
v-model:grid-or-list="gridOrList"
v-model:search="search"
v-model:loading="loading"
:source-apps="selectedApps"
:contributors="selectedMembers"
:project="project"
class="z-[0] relative"
@clear-search="clearSearch"
/>
</div>
</div>
</template>
<script setup lang="ts">
import { useQuery } from '@vue/apollo-composable'
import { projectRoute } from '~~/lib/common/helpers/route'
import type { SourceAppDefinition } from '@speckle/shared'
import type { FormUsersSelectItemFragment } from '~~/lib/common/generated/gql/graphql'
import { projectModelsPageQuery } from '~~/lib/projects/graphql/queries'
import {
useGeneralProjectPageUpdateTracking,
useProjectPageItemViewType
} from '~~/lib/projects/composables/projectPages'
definePageMeta({
middleware: ['require-valid-project']
})
const route = useRoute()
const projectId = computed(() => route.params.id as string)
const selectedMembers = ref([] as FormUsersSelectItemFragment[])
const selectedApps = ref([] as SourceAppDefinition[])
const gridOrList = useProjectPageItemViewType('Models')
const search = ref('')
const loading = ref(false)
const { result } = useQuery(projectModelsPageQuery, () => ({
projectId: projectId.value
}))
const project = computed(() => result.value?.project)
useGeneralProjectPageUpdateTracking({ projectId })
const clearSearch = () => {
search.value = ''
selectedMembers.value = []
selectedApps.value = []
}
const title = computed(() =>
project.value?.name.length ? `Models - ${project.value.name}` : ''
)
useHead({
title
})
</script>
@@ -8,6 +8,8 @@
:help="help"
:rules="rules"
:by="by"
:label-id="labelId"
:button-id="buttonId"
>
<template #something-selected="{ value }">
<ul class="flex flex-wrap gap-1.5 text-xs">
@@ -58,6 +60,8 @@ const props = defineProps<{
multiple?: boolean
rules?: Array<any>
by: string
labelId?: string
buttonId?: string
}>()
const { selectedValue, isArrayValue } = useFormSelectChildInternals<SingleItem>({
@@ -10,6 +10,7 @@
as="div"
>
<ListboxLabel
:id="labelId"
class="block label text-foreground-2 mb-2"
:class="{ 'sr-only': !showLabel }"
>
@@ -17,7 +18,12 @@
</ListboxLabel>
<div :class="buttonsWrapperClasses">
<!-- <div class="relative flex"> -->
<ListboxButton ref="listboxButton" v-slot="{ open }" :class="buttonClasses">
<ListboxButton
:id="buttonId"
ref="listboxButton"
v-slot="{ open }"
:class="buttonClasses"
>
<div class="flex items-center justify-between w-full">
<div class="block truncate grow text-left text-xs sm:text-sm">
<template
@@ -387,6 +393,14 @@ const props = defineProps({
mountMenuOnBody: {
type: Boolean,
default: false
},
labelId: {
type: String,
default: undefined
},
buttonId: {
type: String,
default: undefined
}
})
@@ -11,6 +11,8 @@
:filter-predicate="searchFilterPredicate"
:clearable="clearable"
:help="help"
:label-id="labelId"
:button-id="buttonId"
by="name"
>
<template #nothing-selected>
@@ -131,6 +133,12 @@ const props = defineProps({
},
help: {
type: String
},
labelId: {
type: String
},
buttonId: {
type: String
}
})
@@ -1,7 +1,7 @@
<template>
<Menu v-slot="{ open: isMenuOpen }" as="div" class="relative inline-block">
<div>
<MenuButton ref="menuButton" class="hidden" @click.stop.prevent />
<MenuButton :id="menuId" ref="menuButton" class="hidden" @click.stop.prevent />
<!-- conditional pointer-events-none is necessary to avoid double events when clicking on the button when the menu is already open -->
<div :class="isMenuOpen ? 'pointer-events-none' : ''">
<slot :toggle="toggle" :open="processOpen(isMenuOpen)" />
@@ -69,6 +69,7 @@ const props = defineProps<{
* 2D array so that items can be grouped with dividers between them
*/
items: LayoutMenuItem[][]
menuId?: string
}>()
const menuItems = ref(null as Nullable<{ el: HTMLDivElement }>)
@@ -7,6 +7,14 @@ import {
BoltIcon,
ChatBubbleLeftRightIcon
} from '@heroicons/vue/24/outline'
import { useStorybookVmodel } from '~~/src/composables/testing'
const items: LayoutPageTabItem[] = [
{ title: 'Models', id: 'models', icon: CubeIcon, count: 300 },
{ title: 'Discussions', id: 'discussions', icon: ChatBubbleLeftRightIcon },
{ title: 'Automations', id: 'automations', icon: BoltIcon, tag: 'New' },
{ title: 'Settings', id: 'settings', icon: Cog6ToothIcon }
]
export default {
component: LayoutPageTabs,
@@ -16,33 +24,47 @@ export default {
component: 'Page tabs component'
}
}
},
argTypes: {
items: {
description: 'Array of items to display in the tab'
},
title: {
description: 'Title of the tab'
},
vertical: {
description: 'Whether to display the tabs vertically'
},
activeItem: {
description: 'The active item model. Not required.'
},
'update:activeItem': {
description: 'Event emitted when the active item changes',
type: 'function',
action: 'v-model:activeItem'
}
}
} as Meta
const items: LayoutPageTabItem[] = [
{ title: 'Models', id: 'models', icon: CubeIcon, count: 300 },
{ title: 'Discussions', id: 'discussions', icon: ChatBubbleLeftRightIcon },
{ title: 'Automations', id: 'automations', icon: BoltIcon, tag: 'New' },
{ title: 'Settings', id: 'settings', icon: Cog6ToothIcon }
]
export const Default: StoryObj = {
render: (args) => ({
render: (args, ctx) => ({
components: { LayoutPageTabs },
setup() {
return { args }
const { model } = useStorybookVmodel({ args, prop: 'activeItem', ctx })
return { args, model }
},
template: `
<div>
<LayoutPageTabs v-slot="{ activeItem }" v-bind="args">
<div>Title: {{ activeItem.title }}</div>
<div>ID: {{ activeItem.id }}</div>
<LayoutPageTabs v-slot="{ activeItem }" v-bind="args" v-model:activeItem="model">
<div>Title: {{ activeItem?.title }}</div>
<div>ID: {{ activeItem?.id }}</div>
</LayoutPageTabs>
</div>`
}),
args: {
items,
title: 'Settings'
title: 'Settings',
activeItem: items[0]
}
}
@@ -53,3 +75,34 @@ export const Vertical: StoryObj = {
vertical: true
}
}
export const WithActiveItemModel: StoryObj = {
...Default,
args: {
...Default.args,
activeItem: items[2]
}
}
export const WithActiveItemModelBlocked: StoryObj = {
...WithActiveItemModel,
render: (args, ctx) => ({
components: { LayoutPageTabs },
setup() {
const { model } = useStorybookVmodel({
args,
prop: 'activeItem',
ctx,
blockChanges: true
})
return { args, model }
},
template: `
<div>
<LayoutPageTabs v-slot="{ activeItem }" v-bind="args" v-model:activeItem="model">
<div>Title: {{ activeItem?.title }}</div>
<div>ID: {{ activeItem?.id }}</div>
</LayoutPageTabs>
</div>`
})
}
@@ -41,12 +41,12 @@
:data-tab-id="item.id"
class="tab-button relative z-10 flex items-center gap-1.5 pb-2 border-b-[2px] border-transparent text-base max-w-max px-2"
:class="[
activeItem.id === item.id
activeItem?.id === item.id
? 'text-primary hover:text-primary'
: 'text-foreground',
vertical ? 'hover:border-outline' : 'hover:border-outline-2'
]"
@click="onTabClick(item)"
@click="setActiveItem(item)"
>
<Component
:is="item.icon"
@@ -58,7 +58,7 @@
v-if="item.count"
class="rounded-full px-2 text-[11px] transition-all min-w-6"
:class="
activeItem.id === item.id
activeItem?.id === item.id
? 'text-primary bg-blue-100'
: 'text-foreground-2 bg-gray-200 dark:bg-foundation'
"
@@ -80,10 +80,10 @@
</div>
</template>
<script setup lang="ts">
import { computed, ref, watch, type CSSProperties } from 'vue'
import { computed, ref, type CSSProperties, onMounted, watch } from 'vue'
import type { LayoutPageTabItem } from '~~/src/helpers/layout/components'
import type { Nullable } from '@speckle/shared'
import { isClient } from '@vueuse/core'
import type { MaybeNullOrUndefined, Nullable } from '@speckle/shared'
const props = defineProps<{
items: LayoutPageTabItem[]
@@ -91,16 +91,11 @@ const props = defineProps<{
title?: string
}>()
const activeItem = defineModel<LayoutPageTabItem>('activeItem', { required: true })
const buttonContainer = ref(null as Nullable<HTMLDivElement>)
const activeItemId = ref<string | null>(null)
const activeItem = computed(() => {
const item = props.items.find((i) => i.id === activeItemId.value)
return item || props.items[0]
})
const activeItemRef = computed(() => {
const id = activeItemId.value
const id = activeItem.value?.id
if (!id) return null
const parent = buttonContainer.value
@@ -119,21 +114,25 @@ const borderStyle = computed(() => {
return style
})
const onTabClick = (item: LayoutPageTabItem) => {
activeItemId.value = item.id
const setActiveItem = (item: LayoutPageTabItem) => {
activeItem.value = item
}
const setActiveItem = (item: MaybeNullOrUndefined<LayoutPageTabItem>) => {
if (!isClient || !item?.id) return
if (isClient) {
// Doing onMounted & watch separately to avoid hydration mismatch
onMounted(() => {
if (props.items.length && !activeItem.value) {
setActiveItem(props.items[0])
}
})
activeItemId.value = item.id
watch(
() => [props.items, activeItem.value] as const,
([newItems, activeItem]) => {
if (newItems.length && !activeItem) {
setActiveItem(newItems[0])
}
}
)
}
watch(
() => props.items,
(newItems) => {
setActiveItem(newItems[0])
},
{ immediate: true }
)
</script>
@@ -69,6 +69,9 @@ const { value, errorMessage } = useField<ModelType>(props.name, props.rules, {
initialValue: props.modelValue || undefined
})
// 'local' was recently removed, but we still want to keep backwards compatibility w/ older vue versions, so have to use this workaround
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
const editMode = defineModel<boolean>('editMode', { local: true })
const modelAsUser = computed(
@@ -0,0 +1,35 @@
import { action } from '@storybook/addon-actions'
import type { StoryContext } from '@storybook/vue3'
import { computed } from 'vue'
/**
* Composable for use in storybook to create a v-model binding that properly reports changes to the Actions tab
* and also updates the model in the Controls tab
*/
export const useStorybookVmodel = (params: {
args: Record<string, unknown>
prop: string
ctx: StoryContext
/**
* Prevents the model from being updated
*/
blockChanges?: boolean
}) => {
const { args, prop, ctx, blockChanges } = params
const storybookAction = action(`update:${prop as string}`)
const modelValue = computed({
get: () => params.args[prop],
set: (newVal) => {
if (!blockChanges) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
ctx.updateArgs({ ...args, [prop]: newVal })
}
storybookAction(JSON.parse(JSON.stringify(newVal))) // parse/stringify to clean up fn refs
}
})
return {
model: modelValue
}
}
@@ -13,7 +13,7 @@ export type LayoutTabItem<I extends string = string> = {
export type LayoutPageTabItem<I extends string = string> = {
title: string
id: I
icon: ConcreteComponent
icon?: ConcreteComponent
count?: number
tag?: string
}
+33 -194
View File
@@ -19103,16 +19103,6 @@ __metadata:
languageName: node
linkType: hard
"@vue/compiler-dom@npm:3.3.4, @vue/compiler-dom@npm:^3.3.0":
version: 3.3.4
resolution: "@vue/compiler-dom@npm:3.3.4"
dependencies:
"@vue/compiler-core": 3.3.4
"@vue/shared": 3.3.4
checksum: 1c2ac0c89de8eef7be1c568d57504e6245adaaec40c2c4d9717bc231ca10bf682d918a3b358d24c786eeaf8e0d7eb8a65f57d9044775a304783fde1d069a1896
languageName: node
linkType: hard
"@vue/compiler-dom@npm:3.3.8, @vue/compiler-dom@npm:^3.3.4, @vue/compiler-dom@npm:^3.3.8":
version: 3.3.8
resolution: "@vue/compiler-dom@npm:3.3.8"
@@ -19133,6 +19123,16 @@ __metadata:
languageName: node
linkType: hard
"@vue/compiler-dom@npm:^3.3.0":
version: 3.3.4
resolution: "@vue/compiler-dom@npm:3.3.4"
dependencies:
"@vue/compiler-core": 3.3.4
"@vue/shared": 3.3.4
checksum: 1c2ac0c89de8eef7be1c568d57504e6245adaaec40c2c4d9717bc231ca10bf682d918a3b358d24c786eeaf8e0d7eb8a65f57d9044775a304783fde1d069a1896
languageName: node
linkType: hard
"@vue/compiler-sfc@npm:2.7.5":
version: 2.7.5
resolution: "@vue/compiler-sfc@npm:2.7.5"
@@ -19144,42 +19144,6 @@ __metadata:
languageName: node
linkType: hard
"@vue/compiler-sfc@npm:3.3.4":
version: 3.3.4
resolution: "@vue/compiler-sfc@npm:3.3.4"
dependencies:
"@babel/parser": ^7.20.15
"@vue/compiler-core": 3.3.4
"@vue/compiler-dom": 3.3.4
"@vue/compiler-ssr": 3.3.4
"@vue/reactivity-transform": 3.3.4
"@vue/shared": 3.3.4
estree-walker: ^2.0.2
magic-string: ^0.30.0
postcss: ^8.1.10
source-map-js: ^1.0.2
checksum: 0a0adfdd3e812f528e25e4b3bbf14b2296b719a8aac609eca42035295527cc253b918a552dc15218e917efef26b7ca94054dc8784a1a18c06c3d4bb4d18ab8b9
languageName: node
linkType: hard
"@vue/compiler-sfc@npm:3.3.8, @vue/compiler-sfc@npm:^3.3.8":
version: 3.3.8
resolution: "@vue/compiler-sfc@npm:3.3.8"
dependencies:
"@babel/parser": ^7.23.0
"@vue/compiler-core": 3.3.8
"@vue/compiler-dom": 3.3.8
"@vue/compiler-ssr": 3.3.8
"@vue/reactivity-transform": 3.3.8
"@vue/shared": 3.3.8
estree-walker: ^2.0.2
magic-string: ^0.30.5
postcss: ^8.4.31
source-map-js: ^1.0.2
checksum: 7f931f3fe3fd117974b20f497267e9c29fea83d5703fe65aad5f0ea63c9563581b186acf02cdd1d85526395f0067dde9d05c5e522d9cffba2168b16c4a9414d9
languageName: node
linkType: hard
"@vue/compiler-sfc@npm:3.4.21":
version: 3.4.21
resolution: "@vue/compiler-sfc@npm:3.4.21"
@@ -19251,6 +19215,24 @@ __metadata:
languageName: node
linkType: hard
"@vue/compiler-sfc@npm:^3.3.8":
version: 3.3.8
resolution: "@vue/compiler-sfc@npm:3.3.8"
dependencies:
"@babel/parser": ^7.23.0
"@vue/compiler-core": 3.3.8
"@vue/compiler-dom": 3.3.8
"@vue/compiler-ssr": 3.3.8
"@vue/reactivity-transform": 3.3.8
"@vue/shared": 3.3.8
estree-walker: ^2.0.2
magic-string: ^0.30.5
postcss: ^8.4.31
source-map-js: ^1.0.2
checksum: 7f931f3fe3fd117974b20f497267e9c29fea83d5703fe65aad5f0ea63c9563581b186acf02cdd1d85526395f0067dde9d05c5e522d9cffba2168b16c4a9414d9
languageName: node
linkType: hard
"@vue/compiler-ssr@npm:3.2.41":
version: 3.2.41
resolution: "@vue/compiler-ssr@npm:3.2.41"
@@ -19281,16 +19263,6 @@ __metadata:
languageName: node
linkType: hard
"@vue/compiler-ssr@npm:3.3.4":
version: 3.3.4
resolution: "@vue/compiler-ssr@npm:3.3.4"
dependencies:
"@vue/compiler-dom": 3.3.4
"@vue/shared": 3.3.4
checksum: 5d1875d55ea864080dd90e5d81a29f93308e312faf00163db5b391b38c2fe799fd3eb58955823dc632f2f8bdd271a4534cc0020646b7f82717be1a8d30dc16e7
languageName: node
linkType: hard
"@vue/compiler-ssr@npm:3.3.8":
version: 3.3.8
resolution: "@vue/compiler-ssr@npm:3.3.8"
@@ -19451,19 +19423,6 @@ __metadata:
languageName: node
linkType: hard
"@vue/reactivity-transform@npm:3.3.4":
version: 3.3.4
resolution: "@vue/reactivity-transform@npm:3.3.4"
dependencies:
"@babel/parser": ^7.20.15
"@vue/compiler-core": 3.3.4
"@vue/shared": 3.3.4
estree-walker: ^2.0.2
magic-string: ^0.30.0
checksum: b425e78b2084ac7037887fbe012dcad5e5963ac9714ae15a04fda1c6766ec8c53ef231de1cfdc4d3cf46bd5d84bfec8ebdccf48da4ff5ee2f4b5084e54f0a1b1
languageName: node
linkType: hard
"@vue/reactivity-transform@npm:3.3.8":
version: 3.3.8
resolution: "@vue/reactivity-transform@npm:3.3.8"
@@ -19477,24 +19436,6 @@ __metadata:
languageName: node
linkType: hard
"@vue/reactivity@npm:3.3.4, @vue/reactivity@npm:^3.3.0":
version: 3.3.4
resolution: "@vue/reactivity@npm:3.3.4"
dependencies:
"@vue/shared": 3.3.4
checksum: 81c3d0c587d23656a57a7a31afb51357274f6512b51baffc67cda183b2361a7e65e646029c26a8bc28587f26b65bba808dcd93cdd3bacab48d2b99d11ad0ec97
languageName: node
linkType: hard
"@vue/reactivity@npm:3.3.8":
version: 3.3.8
resolution: "@vue/reactivity@npm:3.3.8"
dependencies:
"@vue/shared": 3.3.8
checksum: 6c6e83c2c9cd29e230d7d45f8c60f9f344129a8904127c0e403f29c1727fb67ed903379c56f9e9fc4166f5e1ba29202604ac77f011d5e3fe7c8f32d6efe7f12a
languageName: node
linkType: hard
"@vue/reactivity@npm:3.4.21":
version: 3.4.21
resolution: "@vue/reactivity@npm:3.4.21"
@@ -19513,23 +19454,12 @@ __metadata:
languageName: node
linkType: hard
"@vue/runtime-core@npm:3.3.4":
"@vue/reactivity@npm:^3.3.0":
version: 3.3.4
resolution: "@vue/runtime-core@npm:3.3.4"
resolution: "@vue/reactivity@npm:3.3.4"
dependencies:
"@vue/reactivity": 3.3.4
"@vue/shared": 3.3.4
checksum: d402da51269658cba5d857d65fbe322121160bcb1a6fcf03601d5183705e92505c6e90418f491a331ca3e27628f457a6ca7158b9add25f5b0cf5cf53664b8011
languageName: node
linkType: hard
"@vue/runtime-core@npm:3.3.8":
version: 3.3.8
resolution: "@vue/runtime-core@npm:3.3.8"
dependencies:
"@vue/reactivity": 3.3.8
"@vue/shared": 3.3.8
checksum: 14b6a5293a25d80c681829b512be5b749fd66e9de4a5de65c9f7d6c82283d4ecb408e84bc485e214627cdb80d40ac8e9970a885592cec2d50acea29ec2ac6f18
checksum: 81c3d0c587d23656a57a7a31afb51357274f6512b51baffc67cda183b2361a7e65e646029c26a8bc28587f26b65bba808dcd93cdd3bacab48d2b99d11ad0ec97
languageName: node
linkType: hard
@@ -19543,28 +19473,6 @@ __metadata:
languageName: node
linkType: hard
"@vue/runtime-dom@npm:3.3.4":
version: 3.3.4
resolution: "@vue/runtime-dom@npm:3.3.4"
dependencies:
"@vue/runtime-core": 3.3.4
"@vue/shared": 3.3.4
csstype: ^3.1.1
checksum: dac9ada7f6128bcccc031fe5c25d00db94ffb7c011fcb70bada22fa4d889ff842eeb139ab9304bcc52cb5ae9030911a52cb3510b691bb190bbe5fab680b4411a
languageName: node
linkType: hard
"@vue/runtime-dom@npm:3.3.8":
version: 3.3.8
resolution: "@vue/runtime-dom@npm:3.3.8"
dependencies:
"@vue/runtime-core": 3.3.8
"@vue/shared": 3.3.8
csstype: ^3.1.2
checksum: fec87df42b536e52da4ff44a0fc715314db0729d22893ba0d5420368dd0b2b8e4f32b51c2dcf0f1420c4b620a235e86a4b7c70d6a4d68ba28839f91bd18047e7
languageName: node
linkType: hard
"@vue/runtime-dom@npm:3.4.21":
version: 3.4.21
resolution: "@vue/runtime-dom@npm:3.4.21"
@@ -19576,30 +19484,6 @@ __metadata:
languageName: node
linkType: hard
"@vue/server-renderer@npm:3.3.4":
version: 3.3.4
resolution: "@vue/server-renderer@npm:3.3.4"
dependencies:
"@vue/compiler-ssr": 3.3.4
"@vue/shared": 3.3.4
peerDependencies:
vue: 3.3.4
checksum: e8598ed1a44df70edaea0ad6786aea6443b9b3d9266249eec5690401859d72d45a1e29ba3eef20e37a95f020abd5e763088b79070ee848af436a4390a253a37a
languageName: node
linkType: hard
"@vue/server-renderer@npm:3.3.8":
version: 3.3.8
resolution: "@vue/server-renderer@npm:3.3.8"
dependencies:
"@vue/compiler-ssr": 3.3.8
"@vue/shared": 3.3.8
peerDependencies:
vue: 3.3.8
checksum: b0577acc10e3b108b4f631af6b9d349747ac49bcce2e3bac7b237bdeaa295effe8e8ac3547fa5d0dc51e8d419d7553a3cb50c07b21ea9df8c422ee0acd9fbcf9
languageName: node
linkType: hard
"@vue/server-renderer@npm:3.4.21":
version: 3.4.21
resolution: "@vue/server-renderer@npm:3.4.21"
@@ -24576,13 +24460,6 @@ __metadata:
languageName: node
linkType: hard
"csstype@npm:^3.1.1, csstype@npm:^3.1.2":
version: 3.1.2
resolution: "csstype@npm:3.1.2"
checksum: e1a52e6c25c1314d6beef5168da704ab29c5186b877c07d822bd0806717d9a265e8493a2e35ca7e68d0f5d472d43fac1cdce70fd79fd0853dff81f3028d857b5
languageName: node
linkType: hard
"csstype@npm:^3.1.3":
version: 3.1.3
resolution: "csstype@npm:3.1.3"
@@ -47896,14 +47773,7 @@ __metadata:
languageName: node
linkType: hard
"vue@npm:^2.6.10":
version: 2.6.14
resolution: "vue@npm:2.6.14"
checksum: 23524a1bdca094d62cb3491a46317eed75184b5d61d28fa846ea5d2b241c1cc7084fc67ee259d47a50a6d0bbc33ecaceb7bb52bff81312fe7da07263f3419942
languageName: node
linkType: hard
"vue@npm:^2.7.5":
"vue@npm:^2.6.10, vue@npm:^2.7.5":
version: 2.7.5
resolution: "vue@npm:2.7.5"
dependencies:
@@ -47913,38 +47783,7 @@ __metadata:
languageName: node
linkType: hard
"vue@npm:^3.3.4":
version: 3.3.4
resolution: "vue@npm:3.3.4"
dependencies:
"@vue/compiler-dom": 3.3.4
"@vue/compiler-sfc": 3.3.4
"@vue/runtime-dom": 3.3.4
"@vue/server-renderer": 3.3.4
"@vue/shared": 3.3.4
checksum: 58b6c62a66a375ce5df460fcb7ba41b37c8637c635faf06ef472ae4197f412cf9ad83586cd8e3f66c486404fbe8550e694f90ff724a571d1ba78830791099c59
languageName: node
linkType: hard
"vue@npm:^3.3.8":
version: 3.3.8
resolution: "vue@npm:3.3.8"
dependencies:
"@vue/compiler-dom": 3.3.8
"@vue/compiler-sfc": 3.3.8
"@vue/runtime-dom": 3.3.8
"@vue/server-renderer": 3.3.8
"@vue/shared": 3.3.8
peerDependencies:
typescript: "*"
peerDependenciesMeta:
typescript:
optional: true
checksum: 560c18aa37f3f2351de8245f7844611409e7b7cbaabaae0993bdacd6090bb5ca81d89366e324babfdc7063f2312de5b62b56fe8b4dadc586baef8e0968eb6e14
languageName: node
linkType: hard
"vue@npm:^3.4.21":
"vue@npm:^3.3.4, vue@npm:^3.3.8, vue@npm:^3.4.21":
version: 3.4.21
resolution: "vue@npm:3.4.21"
dependencies: