@edgedev/create-edge-app 1.1.29 → 1.2.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agents.md +2 -0
- package/bin/cli.js +13 -5
- package/deploy-services.sh +237 -0
- package/deploy.sh +88 -1
- package/edge/components/cms/blockEditor.vue +748 -7
- package/{pages/app/dashboard/blocks/index.vue → edge/components/cms/blocksManager.vue} +9 -24
- package/edge/components/cms/menu.vue +100 -15
- package/edge/components/cms/site.vue +83 -3
- package/edge/components/cms/sitesManager.vue +110 -0
- package/edge/components/cms/themeEditor.vue +9 -3
- package/edge/components/dashboard.vue +22 -3
- package/edge/components/editor.vue +100 -35
- package/edge/components/organizationMembers.vue +294 -221
- package/edge/components/shad/combobox.vue +2 -2
- package/edge/composables/global.ts +1 -1
- package/edge/routes/cms/dashboard/blocks/index.vue +21 -0
- package/edge/routes/cms/dashboard/sites/index.vue +13 -0
- package/edge/routes/cms/nuxtHooks.js +52 -0
- package/edge/routes/cms/routes.js +56 -0
- package/firebase_init.sh +63 -2
- package/nuxt.config.ts +19 -2
- package/package.json +1 -1
- package/services/.deploy.shared.env.example +12 -0
- package/pages/app/dashboard/sites/index.vue +0 -114
- /package/{pages/app → edge/routes/cms}/dashboard/blocks/[block].vue +0 -0
- /package/{pages/app → edge/routes/cms}/dashboard/media/index.vue +0 -0
- /package/{pages/app → edge/routes/cms}/dashboard/sites/[site]/[[page]].vue +0 -0
- /package/{pages/app → edge/routes/cms}/dashboard/sites/[site].vue +0 -0
- /package/{pages/app → edge/routes/cms}/dashboard/templates/[page].vue +0 -0
- /package/{pages/app/dashboard/templates.vue → edge/routes/cms/dashboard/templates/index.vue} +0 -0
- /package/{pages/app → edge/routes/cms}/dashboard/themes/[theme].vue +0 -0
- /package/{pages/app → edge/routes/cms}/dashboard/themes/index.vue +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup>
|
|
2
|
-
import { Maximize2, Monitor, Smartphone, Tablet } from 'lucide-vue-next'
|
|
2
|
+
import { HelpCircle, Maximize2, Monitor, Smartphone, Tablet } from 'lucide-vue-next'
|
|
3
3
|
import { toTypedSchema } from '@vee-validate/zod'
|
|
4
4
|
import * as z from 'zod'
|
|
5
5
|
const props = defineProps({
|
|
@@ -33,6 +33,7 @@ const state = reactive({
|
|
|
33
33
|
jsonEditorOpen: false,
|
|
34
34
|
jsonEditorContent: '',
|
|
35
35
|
jsonEditorError: '',
|
|
36
|
+
helpOpen: false,
|
|
36
37
|
editingContext: null,
|
|
37
38
|
renderSite: '',
|
|
38
39
|
initialBlocksSeeded: false,
|
|
@@ -523,14 +524,20 @@ watch(() => state.jsonEditorOpen, (open) => {
|
|
|
523
524
|
resetJsonEditorState()
|
|
524
525
|
})
|
|
525
526
|
const sites = computed(() => {
|
|
526
|
-
|
|
527
|
+
const sitesMap = edgeFirebase.data?.[`${edgeGlobal.edgeState.organizationDocPath}/sites`] || {}
|
|
528
|
+
return Object.entries(sitesMap)
|
|
529
|
+
.map(([docId, data]) => ({ docId, ...(data || {}) }))
|
|
530
|
+
.filter(site => site.docId && site.docId !== 'templates')
|
|
527
531
|
})
|
|
528
532
|
|
|
529
533
|
watch (sites, async (newSites) => {
|
|
530
534
|
state.loading = true
|
|
531
|
-
|
|
535
|
+
const selectedSite = String(edgeGlobal.edgeState.blockEditorSite || '').trim()
|
|
536
|
+
const hasSelectedSite = newSites.some(site => site.docId === selectedSite)
|
|
537
|
+
if ((!selectedSite || !hasSelectedSite) && newSites.length > 0)
|
|
532
538
|
edgeGlobal.edgeState.blockEditorSite = newSites[0].docId
|
|
533
|
-
|
|
539
|
+
else if (!newSites.length)
|
|
540
|
+
edgeGlobal.edgeState.blockEditorSite = ''
|
|
534
541
|
await nextTick()
|
|
535
542
|
state.loading = false
|
|
536
543
|
}, { immediate: true, deep: true })
|
|
@@ -607,7 +614,6 @@ const getTagsFromBlocks = computed(() => {
|
|
|
607
614
|
</div>
|
|
608
615
|
</div>
|
|
609
616
|
</template>
|
|
610
|
-
|
|
611
617
|
<template #main="slotProps">
|
|
612
618
|
<div class="pt-4">
|
|
613
619
|
<div class="flex w-full gap-2">
|
|
@@ -655,8 +661,20 @@ const getTagsFromBlocks = computed(() => {
|
|
|
655
661
|
<div class="w-1/2">
|
|
656
662
|
<div class="flex gap-2">
|
|
657
663
|
<div class="w-2/12 mb-3 rounded-md border border-slate-200 bg-white/80 p-3 shadow-sm shadow-slate-200/60 dark:border-slate-800 dark:bg-slate-900/60">
|
|
658
|
-
<div class="
|
|
659
|
-
|
|
664
|
+
<div class="flex flex-col gap-2">
|
|
665
|
+
<edge-shad-button
|
|
666
|
+
type="button"
|
|
667
|
+
size="sm"
|
|
668
|
+
variant="secondary"
|
|
669
|
+
class="w-full h-8 px-2 text-[11px] uppercase tracking-wide gap-2"
|
|
670
|
+
@click="state.helpOpen = true"
|
|
671
|
+
>
|
|
672
|
+
<HelpCircle class="w-4 h-4" />
|
|
673
|
+
Block Help
|
|
674
|
+
</edge-shad-button>
|
|
675
|
+
<div class="text-xs font-semibold uppercase tracking-wide text-slate-600 dark:text-slate-300 whitespace-nowrap">
|
|
676
|
+
Dynamic Content
|
|
677
|
+
</div>
|
|
660
678
|
</div>
|
|
661
679
|
<div class="mt-2 flex flex-wrap gap-2">
|
|
662
680
|
<edge-tooltip
|
|
@@ -729,6 +747,729 @@ const getTagsFromBlocks = computed(() => {
|
|
|
729
747
|
</div>
|
|
730
748
|
</template>
|
|
731
749
|
</edge-editor>
|
|
750
|
+
<Sheet v-model:open="state.helpOpen">
|
|
751
|
+
<SheetContent side="right" class="w-full md:w-1/2 max-w-none sm:max-w-none max-w-2xl">
|
|
752
|
+
<SheetHeader>
|
|
753
|
+
<SheetTitle class="text-left">
|
|
754
|
+
Block Editor Guide
|
|
755
|
+
</SheetTitle>
|
|
756
|
+
<SheetDescription class="text-left text-sm text-muted-foreground">
|
|
757
|
+
Everything about blocks: how fields are built, how data loads, which options exist, and how the editor renders.
|
|
758
|
+
</SheetDescription>
|
|
759
|
+
</SheetHeader>
|
|
760
|
+
<div class="px-6 pb-6">
|
|
761
|
+
<Tabs class="w-full" default-value="guide">
|
|
762
|
+
<TabsList class="w-full mt-3 bg-secondary rounded-sm grid grid-cols-3">
|
|
763
|
+
<TabsTrigger value="guide" class="w-full text-black data-[state=active]:bg-black data-[state=active]:text-white">
|
|
764
|
+
Block Guide
|
|
765
|
+
</TabsTrigger>
|
|
766
|
+
<TabsTrigger value="carousel" class="w-full text-black data-[state=active]:bg-black data-[state=active]:text-white">
|
|
767
|
+
Carousel Usage
|
|
768
|
+
</TabsTrigger>
|
|
769
|
+
<TabsTrigger value="scroll-reveals" class="w-full text-black data-[state=active]:bg-black data-[state=active]:text-white">
|
|
770
|
+
Scroll Reveals
|
|
771
|
+
</TabsTrigger>
|
|
772
|
+
</TabsList>
|
|
773
|
+
|
|
774
|
+
<TabsContent value="guide">
|
|
775
|
+
<div class="h-[calc(100vh-190px)] overflow-y-auto pr-1 pb-6">
|
|
776
|
+
<div class="space-y-8">
|
|
777
|
+
<div class="rounded-md border border-border/60 bg-muted/30 p-3">
|
|
778
|
+
<div class="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
|
|
779
|
+
Quick Menu
|
|
780
|
+
</div>
|
|
781
|
+
<div class="mt-2 flex flex-wrap gap-2 text-xs">
|
|
782
|
+
<a href="#block-overview" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Overview</a>
|
|
783
|
+
<a href="#fields-built" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Fields</a>
|
|
784
|
+
<a href="#basic-tags" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Basic Tags</a>
|
|
785
|
+
<a href="#tag-format" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Tag Format</a>
|
|
786
|
+
<a href="#block-settings" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Settings</a>
|
|
787
|
+
<a href="#input-types" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Inputs</a>
|
|
788
|
+
<a href="#image-fields" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Images</a>
|
|
789
|
+
<a href="#select-options" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Selects</a>
|
|
790
|
+
<a href="#arrays-manual" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Arrays</a>
|
|
791
|
+
<a href="#arrays-firestore" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Firestore</a>
|
|
792
|
+
<a href="#arrays-api" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">API</a>
|
|
793
|
+
<a href="#arrays-filters" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Filters</a>
|
|
794
|
+
<a href="#conditionals" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Conditionals</a>
|
|
795
|
+
<a href="#subarrays" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Subarrays</a>
|
|
796
|
+
<a href="#rendering-rules" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Rendering</a>
|
|
797
|
+
<a href="#loading-tokens" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Loading</a>
|
|
798
|
+
<a href="#validation" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Validation</a>
|
|
799
|
+
<a href="#stored-data" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Stored Data</a>
|
|
800
|
+
<a href="#preview-placeholders" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Preview</a>
|
|
801
|
+
<a href="#json-editor" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">JSON Editor</a>
|
|
802
|
+
<a href="#common-mistakes" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Mistakes</a>
|
|
803
|
+
<a href="#indexes-kv" class="px-2 py-1 rounded border border-border bg-background hover:bg-muted transition">Indexes + KV</a>
|
|
804
|
+
</div>
|
|
805
|
+
</div>
|
|
806
|
+
<section id="block-overview" class="space-y-2">
|
|
807
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
808
|
+
What A Block Is
|
|
809
|
+
</h3>
|
|
810
|
+
<p class="text-sm text-foreground">
|
|
811
|
+
A block is HTML plus special tags. The editor scans those tags and builds the form for CMS users.
|
|
812
|
+
Any tag with a <code>field</code> becomes an editable input.
|
|
813
|
+
</p>
|
|
814
|
+
<p class="text-sm text-foreground">
|
|
815
|
+
Your HTML is the template. The CMS form is the data. The preview renders the data inside the template.
|
|
816
|
+
</p>
|
|
817
|
+
</section>
|
|
818
|
+
|
|
819
|
+
<section id="fields-built" class="space-y-2">
|
|
820
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
821
|
+
How The CMS Builds Fields
|
|
822
|
+
</h3>
|
|
823
|
+
<div class="text-sm text-foreground space-y-1">
|
|
824
|
+
<div>The editor scans the HTML for triple‑brace tags like <code v-pre>{{{#text ...}}}</code>.</div>
|
|
825
|
+
<div>The <code>field</code> key becomes the saved data key.</div>
|
|
826
|
+
<div>Fields appear in the order they are first found in the HTML.</div>
|
|
827
|
+
<div>Only triple‑brace tags create inputs. Plain <code v-pre>{{...}}</code> does not.</div>
|
|
828
|
+
<div>When you edit a block, template meta + stored meta are merged. Filters and limits persist.</div>
|
|
829
|
+
</div>
|
|
830
|
+
</section>
|
|
831
|
+
|
|
832
|
+
<section id="basic-tags" class="space-y-3">
|
|
833
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
834
|
+
Basic Field Tags
|
|
835
|
+
</h3>
|
|
836
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>{{{#text {"field":"headline","value":"Hello","title":"Headline"}}}}
|
|
837
|
+
{{{#textarea {"field":"intro","value":""}}}}
|
|
838
|
+
{{{#richtext {"field":"body","value":""}}}}
|
|
839
|
+
{{{#image {"field":"heroImage","value":"https://example.com/hero.jpg"}}}}</code></pre>
|
|
840
|
+
<div class="text-sm text-foreground space-y-1">
|
|
841
|
+
<div><code>field</code> is the key stored in the block.</div>
|
|
842
|
+
<div><code>value</code> is the default value when nothing is saved yet.</div>
|
|
843
|
+
<div><code>title</code> sets the label shown to CMS users. If missing, the field name is used.</div>
|
|
844
|
+
</div>
|
|
845
|
+
</section>
|
|
846
|
+
|
|
847
|
+
<section id="tag-format" class="space-y-2">
|
|
848
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
849
|
+
Tag Format (Be Exact)
|
|
850
|
+
</h3>
|
|
851
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>{{{#text {"field":"title","value":"My Title","title":"Title"}}}}</code></pre>
|
|
852
|
+
<div class="text-sm text-foreground space-y-1">
|
|
853
|
+
<div>Tags start with <code v-pre>{{{#</code> and end with <code v-pre>}}}</code>.</div>
|
|
854
|
+
<div>Config inside the tag is JSON. Use double quotes around keys and strings.</div>
|
|
855
|
+
<div>Commas are required between fields in the config object.</div>
|
|
856
|
+
</div>
|
|
857
|
+
</section>
|
|
858
|
+
|
|
859
|
+
<section id="block-settings" class="space-y-2">
|
|
860
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
861
|
+
Block Settings (Top Row)
|
|
862
|
+
</h3>
|
|
863
|
+
<div class="text-sm text-foreground space-y-1">
|
|
864
|
+
<div><strong>Name</strong> is the library name of the block.</div>
|
|
865
|
+
<div><strong>Tags</strong> are for filtering blocks in the picker.</div>
|
|
866
|
+
<div><strong>Allowed Themes</strong> limits where this block can be used.</div>
|
|
867
|
+
<div><strong>Synced Block</strong> means edits are shared across all instances.</div>
|
|
868
|
+
</div>
|
|
869
|
+
</section>
|
|
870
|
+
|
|
871
|
+
<section id="input-types" class="space-y-3">
|
|
872
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
873
|
+
Input Types (What CMS Users See)
|
|
874
|
+
</h3>
|
|
875
|
+
<div class="text-sm text-foreground space-y-1">
|
|
876
|
+
<div><code>text</code> → single‑line input (HTML is escaped on render).</div>
|
|
877
|
+
<div><code>textarea</code> → multi‑line input (HTML is escaped on render).</div>
|
|
878
|
+
<div><code>richtext</code> → WYSIWYG editor (HTML is rendered as‑is).</div>
|
|
879
|
+
<div><code>number</code> → number input.</div>
|
|
880
|
+
<div><code>image</code> → image picker + preview.</div>
|
|
881
|
+
<div><code>array</code> → list editor (manual items) or data loader (API/collection).</div>
|
|
882
|
+
</div>
|
|
883
|
+
<p class="text-sm text-foreground">
|
|
884
|
+
Rich text image controls include size buttons, float left/none/right, and a width slider (10–100%).
|
|
885
|
+
</p>
|
|
886
|
+
</section>
|
|
887
|
+
|
|
888
|
+
<section id="image-fields" class="space-y-3">
|
|
889
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
890
|
+
Image Fields (Media Picker)
|
|
891
|
+
</h3>
|
|
892
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>{{{#image {"field":"heroImage","value":"","tags":["Backgrounds"]}}}}</code></pre>
|
|
893
|
+
<div class="text-sm text-foreground space-y-1">
|
|
894
|
+
<div><code>tags</code> filters the media manager to specific tag groups.</div>
|
|
895
|
+
<div>The stored value is the image URL.</div>
|
|
896
|
+
</div>
|
|
897
|
+
</section>
|
|
898
|
+
|
|
899
|
+
<section id="select-options" class="space-y-3">
|
|
900
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
901
|
+
Select / Options Fields
|
|
902
|
+
</h3>
|
|
903
|
+
<p class="text-sm text-foreground">
|
|
904
|
+
Add an <code>option</code> object to a field to show a select. Options can be static or pulled from a collection.
|
|
905
|
+
</p>
|
|
906
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>{{{#text {
|
|
907
|
+
"field":"layout",
|
|
908
|
+
"title":"Layout",
|
|
909
|
+
"option":{
|
|
910
|
+
"field":"layout",
|
|
911
|
+
"options":[{"title":"Left","name":"left"},{"title":"Right","name":"right"}],
|
|
912
|
+
"optionsKey":"title",
|
|
913
|
+
"optionsValue":"name"
|
|
914
|
+
}
|
|
915
|
+
}}}</code></pre>
|
|
916
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>{{{#text {
|
|
917
|
+
"field":"agentId",
|
|
918
|
+
"title":"Agent",
|
|
919
|
+
"option":{
|
|
920
|
+
"field":"agentId",
|
|
921
|
+
"options":"users",
|
|
922
|
+
"optionsKey":"name",
|
|
923
|
+
"optionsValue":"userId",
|
|
924
|
+
"multiple":true
|
|
925
|
+
}
|
|
926
|
+
}}}</code></pre>
|
|
927
|
+
<div class="text-sm text-foreground space-y-1">
|
|
928
|
+
<div><code>options</code> can be a static array or a collection name.</div>
|
|
929
|
+
<div><code>optionsKey</code> is the label shown in the dropdown.</div>
|
|
930
|
+
<div><code>optionsValue</code> is the stored value.</div>
|
|
931
|
+
<div><code>multiple: true</code> saves an array of values.</div>
|
|
932
|
+
</div>
|
|
933
|
+
</section>
|
|
934
|
+
|
|
935
|
+
<section id="arrays-manual" class="space-y-3">
|
|
936
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
937
|
+
Arrays (Manual Lists)
|
|
938
|
+
</h3>
|
|
939
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>{{{#array {"field":"list","value":[]}}}}
|
|
940
|
+
{{item}}
|
|
941
|
+
{{{/array}}}</code></pre>
|
|
942
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>{{{#array {
|
|
943
|
+
"field":"cards",
|
|
944
|
+
"schema":[
|
|
945
|
+
{"field":"title","type":"text"},
|
|
946
|
+
{"field":"body","type":"richtext"},
|
|
947
|
+
{"field":"image","type":"image"}
|
|
948
|
+
],
|
|
949
|
+
"value":[]
|
|
950
|
+
}}}}
|
|
951
|
+
<h3>{{item.title}}</h3>
|
|
952
|
+
<div>{{item.body}}</div>
|
|
953
|
+
<img :src="item.image">
|
|
954
|
+
{{{/array}}}</code></pre>
|
|
955
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>{{{#array {
|
|
956
|
+
"field":"statics",
|
|
957
|
+
"value":[
|
|
958
|
+
"First item",
|
|
959
|
+
"Second item",
|
|
960
|
+
"Third item"
|
|
961
|
+
]
|
|
962
|
+
}}}}
|
|
963
|
+
<li>{{item}}</li>
|
|
964
|
+
{{{/array}}}</code></pre>
|
|
965
|
+
<p class="text-sm text-foreground">
|
|
966
|
+
Use <code>schema</code> to define fields on each array item.
|
|
967
|
+
Supported item UI types: <code>text</code>, <code>textarea</code>, <code>richtext</code>, <code>image</code>, <code>number</code>, <code>option</code>.
|
|
968
|
+
</p>
|
|
969
|
+
<div class="text-sm text-foreground space-y-1">
|
|
970
|
+
<div>Manual arrays show an “Add Entry” form, drag handles to reorder, and delete buttons.</div>
|
|
971
|
+
<div>Use <code>number</code> for numeric input. <code>integer</code>/<code>money</code> only affect display formatting.</div>
|
|
972
|
+
<div><code>limit</code> trims output to the first N items when rendering.</div>
|
|
973
|
+
</div>
|
|
974
|
+
<p class="text-sm text-foreground">
|
|
975
|
+
Inside an array block you render <code v-pre>{{item}}</code> or <code v-pre>{{item.fieldName}}</code>.
|
|
976
|
+
</p>
|
|
977
|
+
<p class="text-sm text-foreground">
|
|
978
|
+
For schema formatting only, you can also use <code>integer</code> or <code>money</code> types.
|
|
979
|
+
</p>
|
|
980
|
+
</section>
|
|
981
|
+
|
|
982
|
+
<section id="arrays-firestore" class="space-y-3">
|
|
983
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
984
|
+
Arrays from Firestore
|
|
985
|
+
</h3>
|
|
986
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>{{{#array {
|
|
987
|
+
"field":"list",
|
|
988
|
+
"schema":[{"field":"name","type":"text"},{"field":"role","type":"text"}],
|
|
989
|
+
"collection":{
|
|
990
|
+
"path":"team",
|
|
991
|
+
"uniqueKey":"{orgId}",
|
|
992
|
+
"query":[{"field":"active","operator":"==","value":true}],
|
|
993
|
+
"order":[{"field":"name","direction":"asc"}]
|
|
994
|
+
},
|
|
995
|
+
"limit":6,
|
|
996
|
+
"value":[]
|
|
997
|
+
}}}}</code></pre>
|
|
998
|
+
<div class="text-sm text-foreground space-y-1">
|
|
999
|
+
<div><code>path</code> is under <code>organizations/{orgId}</code>.</div>
|
|
1000
|
+
<div><code>uniqueKey</code> supports <code>{orgId}</code> and <code>{siteId}</code>.</div>
|
|
1001
|
+
<div><code>query</code> and <code>order</code> map to Firestore filters and sort.</div>
|
|
1002
|
+
<div><code>limit</code> caps the results.</div>
|
|
1003
|
+
</div>
|
|
1004
|
+
</section>
|
|
1005
|
+
|
|
1006
|
+
<section id="arrays-api" class="space-y-3">
|
|
1007
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1008
|
+
Arrays from an API
|
|
1009
|
+
</h3>
|
|
1010
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>{{{#array {
|
|
1011
|
+
"field":"list",
|
|
1012
|
+
"api":"https://api.example.com/items",
|
|
1013
|
+
"apiField":"data",
|
|
1014
|
+
"apiQuery":"?limit=4",
|
|
1015
|
+
"limit":4,
|
|
1016
|
+
"value":[]
|
|
1017
|
+
}}}}</code></pre>
|
|
1018
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1019
|
+
<div><code>api</code> is the base URL without query string.</div>
|
|
1020
|
+
<div><code>apiQuery</code> is appended to the URL.</div>
|
|
1021
|
+
<div><code>apiField</code> is the array field in the response.</div>
|
|
1022
|
+
</div>
|
|
1023
|
+
<p class="text-sm text-foreground">
|
|
1024
|
+
Filters from <code>queryOptions</code> become query string parameters at runtime.
|
|
1025
|
+
</p>
|
|
1026
|
+
</section>
|
|
1027
|
+
|
|
1028
|
+
<section id="arrays-filters" class="space-y-3">
|
|
1029
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1030
|
+
Filters for Arrays (queryOptions)
|
|
1031
|
+
</h3>
|
|
1032
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>"queryOptions":[
|
|
1033
|
+
{
|
|
1034
|
+
"field":"users",
|
|
1035
|
+
"operator":"array-contains-any",
|
|
1036
|
+
"options":"users",
|
|
1037
|
+
"optionsKey":"name",
|
|
1038
|
+
"optionsValue":"userId",
|
|
1039
|
+
"multiple":true
|
|
1040
|
+
}
|
|
1041
|
+
]</code></pre>
|
|
1042
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1043
|
+
<div><code>queryOptions</code> creates filter inputs for CMS users.</div>
|
|
1044
|
+
<div>Selections are stored in <code>meta.queryItems</code> and used in the API/collection query.</div>
|
|
1045
|
+
<div><code>options</code> can be a collection name or static array.</div>
|
|
1046
|
+
<div><code>multiple: true</code> saves an array.</div>
|
|
1047
|
+
</div>
|
|
1048
|
+
</section>
|
|
1049
|
+
|
|
1050
|
+
<section id="conditionals" class="space-y-3">
|
|
1051
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1052
|
+
Conditionals (Inside Arrays)
|
|
1053
|
+
</h3>
|
|
1054
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>{{{#if {"cond":"item.price > 0"} }}}
|
|
1055
|
+
<div>Price: {{item.price}}</div>
|
|
1056
|
+
{{{#else}}}
|
|
1057
|
+
<div>Contact for pricing</div>
|
|
1058
|
+
{{{/if}}}</code></pre>
|
|
1059
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1060
|
+
<div><code>cond</code> works on <code>item.*</code> inside array/subarray templates.</div>
|
|
1061
|
+
<div>Supported operators: <code>==</code>, <code>!=</code>, <code>></code>, <code><</code>, <code>>=</code>, <code><=</code>.</div>
|
|
1062
|
+
</div>
|
|
1063
|
+
</section>
|
|
1064
|
+
|
|
1065
|
+
<section id="subarrays" class="space-y-3">
|
|
1066
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1067
|
+
Subarrays (Nested Lists)
|
|
1068
|
+
</h3>
|
|
1069
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>{{{#array {"field":"items","value":[],"as":"card"}}}}
|
|
1070
|
+
<h3>{{card.title}}</h3>
|
|
1071
|
+
{{{#subarray:child {"field":"item.children","limit":0 }}}}
|
|
1072
|
+
<div>{{child}}</div>
|
|
1073
|
+
{{{/subarray}}}
|
|
1074
|
+
{{{/array}}}</code></pre>
|
|
1075
|
+
<p class="text-sm text-foreground">
|
|
1076
|
+
Use <code>as</code> to set an alias (like <code v-pre>{{card.title}}</code>). Use <code>subarray</code> to loop nested lists.
|
|
1077
|
+
</p>
|
|
1078
|
+
</section>
|
|
1079
|
+
|
|
1080
|
+
<section id="rendering-rules" class="space-y-2">
|
|
1081
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1082
|
+
Rendering Rules
|
|
1083
|
+
</h3>
|
|
1084
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1085
|
+
<div><code>text</code> and <code>textarea</code> output is HTML‑escaped.</div>
|
|
1086
|
+
<div><code>richtext</code> output is inserted as HTML.</div>
|
|
1087
|
+
<div>Array schema types format output (e.g. <code>money</code> formats USD, <code>integer</code> truncates).</div>
|
|
1088
|
+
</div>
|
|
1089
|
+
</section>
|
|
1090
|
+
|
|
1091
|
+
<section id="loading-tokens" class="space-y-2">
|
|
1092
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1093
|
+
Loading Tokens
|
|
1094
|
+
</h3>
|
|
1095
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1096
|
+
<div><code v-pre>{{loading}}</code> is empty while loading and <code>hidden</code> when loaded.</div>
|
|
1097
|
+
<div><code v-pre>{{loaded}}</code> is <code>hidden</code> while loading and empty when loaded.</div>
|
|
1098
|
+
<div>These tokens only change when the block is waiting on API or collection data.</div>
|
|
1099
|
+
</div>
|
|
1100
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>{{{#array {"field":"list","api":"https://api.example.com/items","apiField":"data","value":[]}}}}
|
|
1101
|
+
<div class="skeleton {{loading}}">Loading items...</div>
|
|
1102
|
+
<div class="{{loaded}}">
|
|
1103
|
+
<div>{{item.title}}</div>
|
|
1104
|
+
</div>
|
|
1105
|
+
{{{/array}}}</code></pre>
|
|
1106
|
+
</section>
|
|
1107
|
+
|
|
1108
|
+
<section id="validation" class="space-y-2">
|
|
1109
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1110
|
+
Validation Rules
|
|
1111
|
+
</h3>
|
|
1112
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>{{{#text {"field":"title","validation":{"required":true,"min":5,"max":80}}}}}
|
|
1113
|
+
{{{#array {"field":"items","schema":[{"field":"name","type":"text","validation":{"required":true}}]}}}}</code></pre>
|
|
1114
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1115
|
+
<div><code>required</code>, <code>min</code>, <code>max</code> are supported.</div>
|
|
1116
|
+
<div>For numbers, <code>min</code>/<code>max</code> are numeric. For text/arrays they are length or item count.</div>
|
|
1117
|
+
</div>
|
|
1118
|
+
</section>
|
|
1119
|
+
|
|
1120
|
+
<section id="stored-data" class="space-y-2">
|
|
1121
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1122
|
+
Editor vs Stored Data
|
|
1123
|
+
</h3>
|
|
1124
|
+
<p class="text-sm text-foreground">
|
|
1125
|
+
The editor only shows fields in the current template. If a field is removed, it disappears,
|
|
1126
|
+
but stored data stays. Add the field back later and the old data returns.
|
|
1127
|
+
</p>
|
|
1128
|
+
</section>
|
|
1129
|
+
|
|
1130
|
+
<section id="preview-placeholders" class="space-y-2">
|
|
1131
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1132
|
+
Preview + Placeholders
|
|
1133
|
+
</h3>
|
|
1134
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1135
|
+
<div>Empty fields show placeholder text or images in the preview.</div>
|
|
1136
|
+
<div>Array previews show sample items if the list is empty.</div>
|
|
1137
|
+
<div>Use the viewport buttons to test different screen sizes.</div>
|
|
1138
|
+
</div>
|
|
1139
|
+
</section>
|
|
1140
|
+
|
|
1141
|
+
<section id="json-editor" class="space-y-2">
|
|
1142
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1143
|
+
JSON Field Editor
|
|
1144
|
+
</h3>
|
|
1145
|
+
<p class="text-sm text-foreground">
|
|
1146
|
+
Click a line inside the code editor to open the JSON Field Editor for that tag.
|
|
1147
|
+
Fix JSON errors there and save to update the tag.
|
|
1148
|
+
</p>
|
|
1149
|
+
</section>
|
|
1150
|
+
|
|
1151
|
+
<section id="common-mistakes" class="space-y-2">
|
|
1152
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1153
|
+
Common Mistakes
|
|
1154
|
+
</h3>
|
|
1155
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1156
|
+
<div>Missing a <code>field</code> key in a tag.</div>
|
|
1157
|
+
<div>Invalid JSON (missing commas or quotes).</div>
|
|
1158
|
+
<div>Using a schema object instead of a schema array (the editor expects an array).</div>
|
|
1159
|
+
<div>Using <code>order</code> without the right Firestore index.</div>
|
|
1160
|
+
</div>
|
|
1161
|
+
</section>
|
|
1162
|
+
|
|
1163
|
+
<section id="indexes-kv" class="space-y-3">
|
|
1164
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1165
|
+
Firestore Indexes + KV Sync (Required)
|
|
1166
|
+
</h3>
|
|
1167
|
+
<p class="text-sm text-foreground">
|
|
1168
|
+
If you add a Firestore query (like <code>array-contains</code> + <code>order</code>), you must add the
|
|
1169
|
+
matching composite index in <code>firestore.indexes.json</code>.
|
|
1170
|
+
</p>
|
|
1171
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>{
|
|
1172
|
+
"collectionGroup": "listings",
|
|
1173
|
+
"queryScope": "COLLECTION",
|
|
1174
|
+
"fields": [
|
|
1175
|
+
{
|
|
1176
|
+
"fieldPath": "status",
|
|
1177
|
+
"arrayConfig": "CONTAINS"
|
|
1178
|
+
},
|
|
1179
|
+
{
|
|
1180
|
+
"fieldPath": "doc_created_at",
|
|
1181
|
+
"order": "DESCENDING"
|
|
1182
|
+
}
|
|
1183
|
+
]
|
|
1184
|
+
},</code></pre>
|
|
1185
|
+
<p class="text-sm text-foreground">
|
|
1186
|
+
If you want fast search/filtering in the CMS, you also need a KV mirror in Firebase Functions.
|
|
1187
|
+
Example (use your collection + fields):
|
|
1188
|
+
</p>
|
|
1189
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>exports.onListingWritten = createKvMirrorHandlerFromFields({
|
|
1190
|
+
documentPath: 'organizations/{orgId}/listings',
|
|
1191
|
+
uniqueKey: '{orgId}',
|
|
1192
|
+
indexKeys: ['name', 'city', 'state', 'status'],
|
|
1193
|
+
metadataKeys: ['name', 'city', 'state', 'status', 'price', 'doc_created_at'],
|
|
1194
|
+
})</code></pre>
|
|
1195
|
+
</section>
|
|
1196
|
+
</div>
|
|
1197
|
+
</div>
|
|
1198
|
+
</TabsContent>
|
|
1199
|
+
|
|
1200
|
+
<TabsContent value="carousel">
|
|
1201
|
+
<div class="h-[calc(100vh-190px)] overflow-y-auto pr-1 pb-6">
|
|
1202
|
+
<div class="space-y-6">
|
|
1203
|
+
<section class="space-y-2">
|
|
1204
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1205
|
+
What This Does
|
|
1206
|
+
</h3>
|
|
1207
|
+
<p class="text-sm text-foreground">
|
|
1208
|
+
Add <code>data-carousel</code> markup to any CMS block and the runtime auto-initializes Embla on the client.
|
|
1209
|
+
This is initialized in <code>htmlContent.vue</code> and works inside raw block HTML.
|
|
1210
|
+
</p>
|
|
1211
|
+
</section>
|
|
1212
|
+
|
|
1213
|
+
<section class="space-y-3">
|
|
1214
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1215
|
+
Quick Start
|
|
1216
|
+
</h3>
|
|
1217
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code><div
|
|
1218
|
+
data-carousel
|
|
1219
|
+
class="relative overflow-hidden"
|
|
1220
|
+
data-carousel-autoplay
|
|
1221
|
+
data-carousel-interval="4000"
|
|
1222
|
+
data-carousel-loop
|
|
1223
|
+
data-carousel-slides-to-scroll="1"
|
|
1224
|
+
data-carousel-slides-to-scroll-lg="3"
|
|
1225
|
+
>
|
|
1226
|
+
<div data-carousel-track class="flex">
|
|
1227
|
+
{{{#array {"field":"List","schema":[{"field":"header","type":"text"}],"value":[{"header":"One"},{"header":"Two"},{"header":"Three"},{"header":"Four"}]}}}}
|
|
1228
|
+
<div class="shrink-0 min-w-0 flex-[0_0_100%] lg:flex-[0_0_33.333%] p-4">
|
|
1229
|
+
<div class="bg-white shadow rounded p-6 h-40 flex items-center justify-center">
|
|
1230
|
+
{{item.header}}
|
|
1231
|
+
</div>
|
|
1232
|
+
</div>
|
|
1233
|
+
{{{/array}}}
|
|
1234
|
+
</div>
|
|
1235
|
+
|
|
1236
|
+
<button type="button" data-carousel-prev class="absolute left-2 top-1/2 -translate-y-1/2 p-2 bg-black/60 text-white rounded-full">‹</button>
|
|
1237
|
+
<button type="button" data-carousel-next class="absolute right-2 top-1/2 -translate-y-1/2 p-2 bg-black/60 text-white rounded-full">›</button>
|
|
1238
|
+
<div data-carousel-dots class="mt-3 flex justify-center gap-2"></div>
|
|
1239
|
+
</div></code></pre>
|
|
1240
|
+
</section>
|
|
1241
|
+
|
|
1242
|
+
<section class="space-y-2">
|
|
1243
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1244
|
+
Required Markup
|
|
1245
|
+
</h3>
|
|
1246
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1247
|
+
<div><code>[data-carousel]</code> is the root element.</div>
|
|
1248
|
+
<div><code>[data-carousel-track]</code> is the Embla container and should be <code>display:flex</code>.</div>
|
|
1249
|
+
<div>Each slide should be <code>shrink-0</code> with an explicit basis (for example <code>flex-[0_0_100%]</code>).</div>
|
|
1250
|
+
<div>Keep <code>overflow-hidden</code> on the root, not the track.</div>
|
|
1251
|
+
</div>
|
|
1252
|
+
</section>
|
|
1253
|
+
|
|
1254
|
+
<section class="space-y-3">
|
|
1255
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1256
|
+
Supported Data Attributes
|
|
1257
|
+
</h3>
|
|
1258
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1259
|
+
<div><code>data-carousel-autoplay</code> enables autoplay (off by default).</div>
|
|
1260
|
+
<div><code>data-carousel-interval="MS"</code> autoplay delay in ms (default <code>5000</code>).</div>
|
|
1261
|
+
<div><code>data-carousel-loop</code> enables looping.</div>
|
|
1262
|
+
<div><code>data-carousel-transition="fade"</code> uses Embla Fade plugin.</div>
|
|
1263
|
+
<div><code>data-carousel-fade-duration="MS"</code> fade duration in ms (default <code>200</code>).</div>
|
|
1264
|
+
<div><code>data-carousel-no-pause</code> keeps autoplay running through hover/interaction.</div>
|
|
1265
|
+
<div><code>data-carousel-slides-to-scroll="N"</code> base slidesToScroll (default <code>1</code>).</div>
|
|
1266
|
+
<div><code>data-carousel-slides-to-scroll-md="N"</code> at <code>min-width: 768px</code>.</div>
|
|
1267
|
+
<div><code>data-carousel-slides-to-scroll-lg="N"</code> at <code>min-width: 1024px</code>.</div>
|
|
1268
|
+
<div><code>data-carousel-slides-to-scroll-xl="N"</code> at <code>min-width: 1280px</code>.</div>
|
|
1269
|
+
</div>
|
|
1270
|
+
</section>
|
|
1271
|
+
|
|
1272
|
+
<section class="space-y-2">
|
|
1273
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1274
|
+
Behavior Notes
|
|
1275
|
+
</h3>
|
|
1276
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1277
|
+
<div>When <code>loop</code> is off, runtime uses <code>containScroll: "trimSnaps"</code>.</div>
|
|
1278
|
+
<div>Prev/next controls are optional; in loop mode edge clicks wrap manually.</div>
|
|
1279
|
+
<div>Dots are generated from Embla snap points, not raw slide count.</div>
|
|
1280
|
+
<div>Breakpoints can change snap count, so dots/buttons are rebuilt on <code>reInit</code>.</div>
|
|
1281
|
+
<div>Carousels are initialized once per root and tagged with <code>data-embla="true"</code>.</div>
|
|
1282
|
+
</div>
|
|
1283
|
+
</section>
|
|
1284
|
+
|
|
1285
|
+
<section class="space-y-3">
|
|
1286
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1287
|
+
Common Patterns
|
|
1288
|
+
</h3>
|
|
1289
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code><!-- Single-slide fade -->
|
|
1290
|
+
<div data-carousel data-carousel-transition="fade" data-carousel-fade-duration="800">...</div>
|
|
1291
|
+
|
|
1292
|
+
<!-- Multi-up desktop paging by 3 -->
|
|
1293
|
+
<div data-carousel data-carousel-slides-to-scroll="1" data-carousel-slides-to-scroll-lg="3">...</div></code></pre>
|
|
1294
|
+
</section>
|
|
1295
|
+
</div>
|
|
1296
|
+
</div>
|
|
1297
|
+
</TabsContent>
|
|
1298
|
+
|
|
1299
|
+
<TabsContent value="scroll-reveals">
|
|
1300
|
+
<div class="h-[calc(100vh-190px)] overflow-y-auto pr-1 pb-6">
|
|
1301
|
+
<div class="space-y-6">
|
|
1302
|
+
<section class="space-y-2">
|
|
1303
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1304
|
+
What This Does
|
|
1305
|
+
</h3>
|
|
1306
|
+
<p class="text-sm text-foreground">
|
|
1307
|
+
Add classes to HTML elements in CMS blocks to trigger scroll reveal animations automatically.
|
|
1308
|
+
</p>
|
|
1309
|
+
</section>
|
|
1310
|
+
|
|
1311
|
+
<section class="space-y-3">
|
|
1312
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1313
|
+
Quick Start
|
|
1314
|
+
</h3>
|
|
1315
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code><div class="sr sr-up sr-delay-150 sr-dur-800 sr-dist-30">
|
|
1316
|
+
I animate on scroll
|
|
1317
|
+
</div></code></pre>
|
|
1318
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code><div class="sr-group sr-up sr-interval-120 sr-dur-700">
|
|
1319
|
+
<div class="sr-item">Item 1</div>
|
|
1320
|
+
<div class="sr-item">Item 2</div>
|
|
1321
|
+
<div class="sr-item">Item 3</div>
|
|
1322
|
+
</div></code></pre>
|
|
1323
|
+
</section>
|
|
1324
|
+
|
|
1325
|
+
<section class="space-y-2">
|
|
1326
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1327
|
+
Required Base Classes
|
|
1328
|
+
</h3>
|
|
1329
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1330
|
+
<div><code>sr</code>: reveal this element.</div>
|
|
1331
|
+
<div><code>sr-group</code>: reveal/stagger a group of children with shared options.</div>
|
|
1332
|
+
<div><code>sr-item</code>: child element inside <code>sr-group</code>.</div>
|
|
1333
|
+
</div>
|
|
1334
|
+
</section>
|
|
1335
|
+
|
|
1336
|
+
<section class="space-y-2">
|
|
1337
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1338
|
+
Direction / Origin
|
|
1339
|
+
</h3>
|
|
1340
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1341
|
+
<div><code>sr-up</code>, <code>sr-down</code>, <code>sr-left</code>, <code>sr-right</code></div>
|
|
1342
|
+
<div><code>sr-origin-top</code>, <code>sr-origin-right</code>, <code>sr-origin-bottom</code>, <code>sr-origin-left</code></div>
|
|
1343
|
+
</div>
|
|
1344
|
+
</section>
|
|
1345
|
+
|
|
1346
|
+
<section class="space-y-2">
|
|
1347
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1348
|
+
Timing
|
|
1349
|
+
</h3>
|
|
1350
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1351
|
+
<div><code>sr-delay-200</code> (ms)</div>
|
|
1352
|
+
<div><code>sr-dur-700</code> or <code>sr-duration-700</code> (ms)</div>
|
|
1353
|
+
<div><code>sr-interval-120</code> or <code>sr-stagger-120</code> (ms between items)</div>
|
|
1354
|
+
</div>
|
|
1355
|
+
</section>
|
|
1356
|
+
|
|
1357
|
+
<section class="space-y-2">
|
|
1358
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1359
|
+
Movement / Transform
|
|
1360
|
+
</h3>
|
|
1361
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1362
|
+
<div><code>sr-dist-24</code> or <code>sr-distance-24</code> (px when numeric)</div>
|
|
1363
|
+
<div><code>sr-opacity-0.2</code></div>
|
|
1364
|
+
<div><code>sr-scale-0.9</code></div>
|
|
1365
|
+
<div><code>sr-rotate-10</code>, <code>sr-rotate-x-15</code>, <code>sr-rotate-y-15</code>, <code>sr-rotate-z-15</code></div>
|
|
1366
|
+
</div>
|
|
1367
|
+
</section>
|
|
1368
|
+
|
|
1369
|
+
<section class="space-y-2">
|
|
1370
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1371
|
+
View Trigger
|
|
1372
|
+
</h3>
|
|
1373
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1374
|
+
<div><code>sr-view-factor-0.2</code> or <code>sr-viewfactor-0.2</code></div>
|
|
1375
|
+
<div><code>sr-view-offset-top-80</code>, <code>sr-view-offset-right-40</code>, <code>sr-view-offset-bottom-80</code>, <code>sr-view-offset-left-40</code></div>
|
|
1376
|
+
</div>
|
|
1377
|
+
</section>
|
|
1378
|
+
|
|
1379
|
+
<section class="space-y-2">
|
|
1380
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1381
|
+
Behavior
|
|
1382
|
+
</h3>
|
|
1383
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1384
|
+
<div><code>sr-reset</code>, <code>sr-no-reset</code></div>
|
|
1385
|
+
<div><code>sr-cleanup</code>, <code>sr-no-cleanup</code></div>
|
|
1386
|
+
<div><code>sr-use-delay-always</code>, <code>sr-use-delay-once</code>, <code>sr-use-delay-onload</code></div>
|
|
1387
|
+
</div>
|
|
1388
|
+
</section>
|
|
1389
|
+
|
|
1390
|
+
<section class="space-y-2">
|
|
1391
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1392
|
+
Device Targeting
|
|
1393
|
+
</h3>
|
|
1394
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1395
|
+
<div><code>sr-no-mobile</code>, <code>sr-no-desktop</code></div>
|
|
1396
|
+
<div><code>sr-mobile-true</code>, <code>sr-mobile-false</code></div>
|
|
1397
|
+
<div><code>sr-desktop-true</code>, <code>sr-desktop-false</code></div>
|
|
1398
|
+
</div>
|
|
1399
|
+
</section>
|
|
1400
|
+
|
|
1401
|
+
<section class="space-y-2">
|
|
1402
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1403
|
+
Easing
|
|
1404
|
+
</h3>
|
|
1405
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1406
|
+
<div><code>sr-ease-linear</code>, <code>sr-ease-in</code>, <code>sr-ease-out</code>, <code>sr-ease-in-out</code></div>
|
|
1407
|
+
<div><code>sr-easing-...</code> for advanced raw tokens</div>
|
|
1408
|
+
</div>
|
|
1409
|
+
</section>
|
|
1410
|
+
|
|
1411
|
+
<section class="space-y-2">
|
|
1412
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1413
|
+
Container Targeting (Advanced)
|
|
1414
|
+
</h3>
|
|
1415
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1416
|
+
<div><code>sr-container-id-main</code></div>
|
|
1417
|
+
<div><code>sr-container-class-scroll-area</code></div>
|
|
1418
|
+
<div><code>sr-container-tag-main</code></div>
|
|
1419
|
+
</div>
|
|
1420
|
+
</section>
|
|
1421
|
+
|
|
1422
|
+
<section class="space-y-2">
|
|
1423
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1424
|
+
Defaults
|
|
1425
|
+
</h3>
|
|
1426
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1427
|
+
<div><code>origin: bottom</code></div>
|
|
1428
|
+
<div><code>distance: 24px</code></div>
|
|
1429
|
+
<div><code>duration: 700</code></div>
|
|
1430
|
+
<div><code>easing: cubic-bezier(0.5, 0, 0, 1)</code></div>
|
|
1431
|
+
<div><code>viewFactor: 0.15</code></div>
|
|
1432
|
+
<div><code>reset: false</code></div>
|
|
1433
|
+
<div><code>cleanup: false</code></div>
|
|
1434
|
+
<div><code>mobile: true</code></div>
|
|
1435
|
+
<div><code>desktop: true</code></div>
|
|
1436
|
+
</div>
|
|
1437
|
+
</section>
|
|
1438
|
+
|
|
1439
|
+
<section class="space-y-3">
|
|
1440
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1441
|
+
Callback Hooks (Advanced)
|
|
1442
|
+
</h3>
|
|
1443
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1444
|
+
<div><code>sr-before-reveal-{key}</code></div>
|
|
1445
|
+
<div><code>sr-after-reveal-{key}</code></div>
|
|
1446
|
+
<div><code>sr-before-reset-{key}</code></div>
|
|
1447
|
+
<div><code>sr-after-reset-{key}</code></div>
|
|
1448
|
+
</div>
|
|
1449
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code>window.__srCallbacks = {
|
|
1450
|
+
myHook: (el) => { console.log('revealed', el) },
|
|
1451
|
+
}</code></pre>
|
|
1452
|
+
<pre v-pre class="rounded-md bg-muted p-3 text-xs overflow-auto"><code><div class="sr sr-up sr-after-reveal-myHook">...</div></code></pre>
|
|
1453
|
+
</section>
|
|
1454
|
+
|
|
1455
|
+
<section class="space-y-2">
|
|
1456
|
+
<h3 class="text-sm font-semibold uppercase tracking-wide text-muted-foreground">
|
|
1457
|
+
Best Practices
|
|
1458
|
+
</h3>
|
|
1459
|
+
<div class="text-sm text-foreground space-y-1">
|
|
1460
|
+
<div>Always include <code>sr</code> for single elements.</div>
|
|
1461
|
+
<div>For staggered lists, use <code>sr-group</code> on parent and <code>sr-item</code> on children.</div>
|
|
1462
|
+
<div>Keep class names lowercase.</div>
|
|
1463
|
+
<div>Prefer <code>sr-ease-*</code> presets unless you need advanced easing.</div>
|
|
1464
|
+
</div>
|
|
1465
|
+
</section>
|
|
1466
|
+
</div>
|
|
1467
|
+
</div>
|
|
1468
|
+
</TabsContent>
|
|
1469
|
+
</Tabs>
|
|
1470
|
+
</div>
|
|
1471
|
+
</SheetContent>
|
|
1472
|
+
</Sheet>
|
|
732
1473
|
<Sheet
|
|
733
1474
|
v-model:open="state.jsonEditorOpen"
|
|
734
1475
|
>
|