@velastack/cms 0.6.4 → 0.7.0
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/README.md +125 -0
- package/dist/components/admin-bar/admin-bar.css +1 -1
- package/dist/components/admin-bar/field-input.svelte +1 -1
- package/dist/components/admin-bar/structured-editor-popover.svelte +21 -3
- package/dist/components/admin-bar/ui/badge/badge.svelte.d.ts +1 -1
- package/dist/components/cms/cms-form-controls.svelte +23 -0
- package/dist/components/cms/cms-form-controls.svelte.d.ts +13 -0
- package/dist/components/cms/cms-form-fields.svelte +68 -0
- package/dist/components/cms/cms-form-fields.svelte.d.ts +21 -0
- package/dist/components/cms/cms-form.svelte +99 -0
- package/dist/components/cms/cms-form.svelte.d.ts +35 -0
- package/dist/components/cms/cms-whatsapp-cart.svelte +202 -0
- package/dist/components/cms/cms-whatsapp-cart.svelte.d.ts +49 -0
- package/dist/components/cms/cms-whatsapp-form-editable.svelte +43 -0
- package/dist/components/cms/cms-whatsapp-form-editable.svelte.d.ts +17 -0
- package/dist/components/cms/cms-whatsapp-form.svelte +167 -0
- package/dist/components/cms/cms-whatsapp-form.svelte.d.ts +45 -0
- package/dist/components/cms/index.d.ts +5 -0
- package/dist/components/cms/index.js +3 -0
- package/dist/components/cms/use-form-target.svelte.d.ts +18 -0
- package/dist/components/cms/use-form-target.svelte.js +68 -0
- package/dist/components/cms/whatsapp-cart.svelte.d.ts +46 -0
- package/dist/components/cms/whatsapp-cart.svelte.js +86 -0
- package/dist/core/forms/order-message.d.ts +10 -0
- package/dist/core/forms/order-message.js +13 -0
- package/dist/core/forms/target.d.ts +9 -0
- package/dist/core/forms/target.js +9 -0
- package/dist/core/shapes/form-fields.d.ts +20 -0
- package/dist/core/shapes/form-fields.js +117 -0
- package/dist/core/shapes/index.d.ts +4 -0
- package/dist/core/shapes/index.js +2 -0
- package/dist/core/shapes/whatsapp.d.ts +8 -0
- package/dist/core/shapes/whatsapp.js +28 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +8 -0
- package/package.json +2 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The cart a `CmsWhatsAppCart` checks out: items kept in the browser, which
|
|
3
|
+
* checkout turns into a WhatsApp message to the business. Nothing is paid on
|
|
4
|
+
* the site, so what is stored is what the visitor asked for, not a price.
|
|
5
|
+
*
|
|
6
|
+
* Module state, filled from localStorage by `load()` in the browser only, so
|
|
7
|
+
* the server renders an empty cart and hydration agrees. Its methods run from
|
|
8
|
+
* event handlers, never during a server render.
|
|
9
|
+
*/
|
|
10
|
+
import { type OrderLine } from '@velastack/whatsapp';
|
|
11
|
+
/** A line in the cart; its `price` is in the site's currency. */
|
|
12
|
+
export type WhatsAppCartItem = OrderLine & {
|
|
13
|
+
/** The template's id for the item (a slug); lines with the same id merge. */
|
|
14
|
+
id: string;
|
|
15
|
+
};
|
|
16
|
+
/** An order sent to WhatsApp, kept until the visitor says they're done. */
|
|
17
|
+
export type WhatsAppPlacedOrder = {
|
|
18
|
+
ref: string;
|
|
19
|
+
/** The chat link, to open it again. */
|
|
20
|
+
href: string;
|
|
21
|
+
};
|
|
22
|
+
declare class WhatsAppCart {
|
|
23
|
+
#private;
|
|
24
|
+
items: WhatsAppCartItem[];
|
|
25
|
+
/** For the template's drawer or sheet, if it has one. */
|
|
26
|
+
open: boolean;
|
|
27
|
+
placed: WhatsAppPlacedOrder | null;
|
|
28
|
+
count: number;
|
|
29
|
+
/**
|
|
30
|
+
* The currency every price is in: the business's own. `CmsWhatsAppCart`
|
|
31
|
+
* sets it from its `currency` prop or the `currency` site option.
|
|
32
|
+
*/
|
|
33
|
+
currency: string;
|
|
34
|
+
/** Only when every item has a price; otherwise the business quotes. */
|
|
35
|
+
subtotal: number | undefined;
|
|
36
|
+
/** Reads the saved cart, once, in the browser. Safe to call from any component's mount. */
|
|
37
|
+
load(): void;
|
|
38
|
+
add(item: Omit<WhatsAppCartItem, 'quantity'>, quantity?: number): void;
|
|
39
|
+
/** A quantity of 0 removes the item. */
|
|
40
|
+
setQuantity(id: string, quantity: number): void;
|
|
41
|
+
remove(id: string): void;
|
|
42
|
+
place(order: WhatsAppPlacedOrder): void;
|
|
43
|
+
clear(): void;
|
|
44
|
+
}
|
|
45
|
+
export declare const whatsappCart: WhatsAppCart;
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The cart a `CmsWhatsAppCart` checks out: items kept in the browser, which
|
|
3
|
+
* checkout turns into a WhatsApp message to the business. Nothing is paid on
|
|
4
|
+
* the site, so what is stored is what the visitor asked for, not a price.
|
|
5
|
+
*
|
|
6
|
+
* Module state, filled from localStorage by `load()` in the browser only, so
|
|
7
|
+
* the server renders an empty cart and hydration agrees. Its methods run from
|
|
8
|
+
* event handlers, never during a server render.
|
|
9
|
+
*/
|
|
10
|
+
import { orderSubtotal } from '@velastack/whatsapp';
|
|
11
|
+
const STORAGE_KEY = 'vela-cms:whatsapp-cart';
|
|
12
|
+
class WhatsAppCart {
|
|
13
|
+
items = $state([]);
|
|
14
|
+
/** For the template's drawer or sheet, if it has one. */
|
|
15
|
+
open = $state(false);
|
|
16
|
+
placed = $state(null);
|
|
17
|
+
count = $derived(this.items.reduce((total, item) => total + item.quantity, 0));
|
|
18
|
+
/**
|
|
19
|
+
* The currency every price is in: the business's own. `CmsWhatsAppCart`
|
|
20
|
+
* sets it from its `currency` prop or the `currency` site option.
|
|
21
|
+
*/
|
|
22
|
+
currency = $state('');
|
|
23
|
+
/** Only when every item has a price; otherwise the business quotes. */
|
|
24
|
+
subtotal = $derived(this.currency ? orderSubtotal(this.items, this.currency) : undefined);
|
|
25
|
+
#loaded = false;
|
|
26
|
+
/** Reads the saved cart, once, in the browser. Safe to call from any component's mount. */
|
|
27
|
+
load() {
|
|
28
|
+
if (typeof localStorage === 'undefined' || this.#loaded)
|
|
29
|
+
return;
|
|
30
|
+
this.#loaded = true;
|
|
31
|
+
try {
|
|
32
|
+
const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '{}');
|
|
33
|
+
if (Array.isArray(saved?.items))
|
|
34
|
+
this.items = saved.items;
|
|
35
|
+
if (saved?.placed)
|
|
36
|
+
this.placed = saved.placed;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Unreadable or blocked storage starts an empty cart.
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
add(item, quantity = 1) {
|
|
43
|
+
this.load();
|
|
44
|
+
// Adding after an order was sent starts the next one.
|
|
45
|
+
if (this.placed)
|
|
46
|
+
this.clear();
|
|
47
|
+
const line = this.items.find((line) => line.id === item.id);
|
|
48
|
+
if (line)
|
|
49
|
+
line.quantity += quantity;
|
|
50
|
+
else
|
|
51
|
+
this.items.push({ ...item, quantity });
|
|
52
|
+
this.#save();
|
|
53
|
+
}
|
|
54
|
+
/** A quantity of 0 removes the item. */
|
|
55
|
+
setQuantity(id, quantity) {
|
|
56
|
+
if (quantity <= 0)
|
|
57
|
+
return this.remove(id);
|
|
58
|
+
const line = this.items.find((line) => line.id === id);
|
|
59
|
+
if (!line)
|
|
60
|
+
return;
|
|
61
|
+
line.quantity = quantity;
|
|
62
|
+
this.#save();
|
|
63
|
+
}
|
|
64
|
+
remove(id) {
|
|
65
|
+
this.items = this.items.filter((line) => line.id !== id);
|
|
66
|
+
this.#save();
|
|
67
|
+
}
|
|
68
|
+
place(order) {
|
|
69
|
+
this.placed = order;
|
|
70
|
+
this.#save();
|
|
71
|
+
}
|
|
72
|
+
clear() {
|
|
73
|
+
this.items = [];
|
|
74
|
+
this.placed = null;
|
|
75
|
+
this.#save();
|
|
76
|
+
}
|
|
77
|
+
#save() {
|
|
78
|
+
try {
|
|
79
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify({ items: $state.snapshot(this.items), placed: this.placed }));
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Storage can be full or blocked; the cart still works for this visit.
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export const whatsappCart = new WhatsAppCart();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The message a `CmsWhatsAppCart` sends: the editor's template, filled from
|
|
3
|
+
* the order by `@velastack/whatsapp`'s `orderPlaceholders`.
|
|
4
|
+
*/
|
|
5
|
+
/** The `{placeholders}` an order message can use. */
|
|
6
|
+
export declare const ORDER_PLACEHOLDERS: string[];
|
|
7
|
+
/** Until the editor writes one. Lines left empty (no total, no note) are dropped. */
|
|
8
|
+
export declare const DEFAULT_ORDER_MESSAGE = "Hi! I'd like to place an order ({ref}):\n\n{items}\n\n{total}\n\n{name} \u00B7 {phone}\n{note}";
|
|
9
|
+
/** Drops the blank lines and dangling spaces that empty placeholders leave behind. */
|
|
10
|
+
export declare const tidyMessage: (message: string) => string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The message a `CmsWhatsAppCart` sends: the editor's template, filled from
|
|
3
|
+
* the order by `@velastack/whatsapp`'s `orderPlaceholders`.
|
|
4
|
+
*/
|
|
5
|
+
/** The `{placeholders}` an order message can use. */
|
|
6
|
+
export const ORDER_PLACEHOLDERS = ['ref', 'items', 'total', 'name', 'phone', 'note'];
|
|
7
|
+
/** Until the editor writes one. Lines left empty (no total, no note) are dropped. */
|
|
8
|
+
export const DEFAULT_ORDER_MESSAGE = "Hi! I'd like to place an order ({ref}):\n\n{items}\n\n{total}\n\n{name} · {phone}\n{note}";
|
|
9
|
+
/** Drops the blank lines and dangling spaces that empty placeholders leave behind. */
|
|
10
|
+
export const tidyMessage = (message) => message
|
|
11
|
+
.replace(/[ \t]+$/gm, '')
|
|
12
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
13
|
+
.trim();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a form posts when the template names a hosted form by `key`:
|
|
3
|
+
* velastack.dev serves a project's hosted forms beside its CMS, at
|
|
4
|
+
* `/v1/projects/<id>/forms/<key>`. Any other endpoint shape has no hosted
|
|
5
|
+
* forms, so `hostedFormUrl` gives `null` and the template passes `action`.
|
|
6
|
+
*/
|
|
7
|
+
export { hostedFormUrl } from '@velastack/whatsapp';
|
|
8
|
+
/** Fields a form carries for the endpoint rather than the submission. */
|
|
9
|
+
export declare const isControlField: (name: string) => boolean;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a form posts when the template names a hosted form by `key`:
|
|
3
|
+
* velastack.dev serves a project's hosted forms beside its CMS, at
|
|
4
|
+
* `/v1/projects/<id>/forms/<key>`. Any other endpoint shape has no hosted
|
|
5
|
+
* forms, so `hostedFormUrl` gives `null` and the template passes `action`.
|
|
6
|
+
*/
|
|
7
|
+
export { hostedFormUrl } from '@velastack/whatsapp';
|
|
8
|
+
/** Fields a form carries for the endpoint rather than the submission. */
|
|
9
|
+
export const isControlField = (name) => name.startsWith('_');
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type ListValue } from './form.js';
|
|
2
|
+
export declare const FORM_FIELD_TYPES: readonly ["text", "email", "tel", "number", "date", "textarea", "select", "checkbox"];
|
|
3
|
+
export type CmsFormFieldType = (typeof FORM_FIELD_TYPES)[number];
|
|
4
|
+
export type CmsFormFieldItem = {
|
|
5
|
+
id: string;
|
|
6
|
+
/** The submitted field name. */
|
|
7
|
+
name: string;
|
|
8
|
+
label: string;
|
|
9
|
+
type: CmsFormFieldType;
|
|
10
|
+
required: boolean;
|
|
11
|
+
placeholder: string;
|
|
12
|
+
/** `select`: the choices, in order. */
|
|
13
|
+
options: string[];
|
|
14
|
+
};
|
|
15
|
+
export type CmsFormFieldsValue = ListValue<CmsFormFieldItem>;
|
|
16
|
+
/** `Preferred date` → `preferred_date`; a label with no latin letters → `field`. */
|
|
17
|
+
export declare const fieldNameFromLabel: (label: string) => string;
|
|
18
|
+
export declare const cmsFormFields: import("./form.js").FormShape<CmsFormFieldsValue>;
|
|
19
|
+
/** What a template renders: every field, in order. */
|
|
20
|
+
export declare const formFieldsView: (value: CmsFormFieldsValue) => CmsFormFieldItem[];
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `CmsFormFields`: the inputs of a form, as content an editor can add to,
|
|
3
|
+
* remove from, reorder and retype.
|
|
4
|
+
*
|
|
5
|
+
* A field's `name` is the key its answer is stored under, so it must not
|
|
6
|
+
* follow later label edits or a form's submissions would split across keys.
|
|
7
|
+
* Fields from the template's fallback carry their own names. A field an
|
|
8
|
+
* editor adds has none until its label is first saved; reads derive one from
|
|
9
|
+
* the label, and the next save through the popover writes it down, after
|
|
10
|
+
* which renaming the label leaves it alone. The editor never shows `name`.
|
|
11
|
+
*/
|
|
12
|
+
import { asBoolean, asEnum, asItems, asString, asStrings, isPlainObject } from '../structured.js';
|
|
13
|
+
import { defineForm } from './form.js';
|
|
14
|
+
export const FORM_FIELD_TYPES = [
|
|
15
|
+
'text',
|
|
16
|
+
'email',
|
|
17
|
+
'tel',
|
|
18
|
+
'number',
|
|
19
|
+
'date',
|
|
20
|
+
'textarea',
|
|
21
|
+
'select',
|
|
22
|
+
'checkbox'
|
|
23
|
+
];
|
|
24
|
+
/** `Preferred date` → `preferred_date`; a label with no latin letters → `field`. */
|
|
25
|
+
export const fieldNameFromLabel = (label) => label
|
|
26
|
+
.normalize('NFKD')
|
|
27
|
+
.replace(/[̀-ͯ]/g, '')
|
|
28
|
+
.toLowerCase()
|
|
29
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
30
|
+
.replace(/^_+|_+$/g, '') || 'field';
|
|
31
|
+
/**
|
|
32
|
+
* Give every field a distinct name. Stored names are kept and claimed
|
|
33
|
+
* first, so a newly added field can never take one an existing field's
|
|
34
|
+
* submissions already use; derived names get `_2`, `_3`, … on a clash.
|
|
35
|
+
*/
|
|
36
|
+
const assignNames = (items) => {
|
|
37
|
+
const taken = new Set();
|
|
38
|
+
const named = items.map((item) => {
|
|
39
|
+
if (!item.stored || taken.has(item.stored))
|
|
40
|
+
return { ...item, name: '' };
|
|
41
|
+
taken.add(item.stored);
|
|
42
|
+
return { ...item, name: item.stored };
|
|
43
|
+
});
|
|
44
|
+
return named.map(({ stored: _stored, ...item }) => {
|
|
45
|
+
if (item.name)
|
|
46
|
+
return item;
|
|
47
|
+
const base = fieldNameFromLabel(item.label);
|
|
48
|
+
let name = base;
|
|
49
|
+
for (let n = 2; taken.has(name); n++)
|
|
50
|
+
name = `${base}_${n}`;
|
|
51
|
+
taken.add(name);
|
|
52
|
+
return { ...item, name };
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
export const cmsFormFields = defineForm({
|
|
56
|
+
component: 'CmsFormFields',
|
|
57
|
+
label: 'Form fields',
|
|
58
|
+
version: 1,
|
|
59
|
+
translatable: [],
|
|
60
|
+
items: { key: 'items', translatable: ['label', 'placeholder', 'options'] },
|
|
61
|
+
normalize: (raw) => {
|
|
62
|
+
const items = Array.isArray(raw) ? raw : isPlainObject(raw) ? raw.items : [];
|
|
63
|
+
return {
|
|
64
|
+
v: 1,
|
|
65
|
+
items: assignNames(asItems(items, (r) => ({
|
|
66
|
+
stored: asString(r.name).trim(),
|
|
67
|
+
name: '',
|
|
68
|
+
label: asString(r.label),
|
|
69
|
+
type: asEnum(r.type, FORM_FIELD_TYPES, 'text'),
|
|
70
|
+
required: asBoolean(r.required),
|
|
71
|
+
placeholder: asString(r.placeholder),
|
|
72
|
+
options: asStrings(r.options)
|
|
73
|
+
})))
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
empty: () => ({ v: 1, items: [] }),
|
|
77
|
+
fields: [
|
|
78
|
+
{
|
|
79
|
+
key: 'items',
|
|
80
|
+
label: 'Fields',
|
|
81
|
+
type: 'list',
|
|
82
|
+
itemLabel: 'field',
|
|
83
|
+
titleKey: 'label',
|
|
84
|
+
blank: () => ({ label: '', type: 'text', required: false, placeholder: '', options: [] }),
|
|
85
|
+
fields: [
|
|
86
|
+
{ key: 'label', label: 'Label', type: 'text' },
|
|
87
|
+
{
|
|
88
|
+
key: 'type',
|
|
89
|
+
label: 'Type',
|
|
90
|
+
type: 'enum',
|
|
91
|
+
values: FORM_FIELD_TYPES,
|
|
92
|
+
names: {
|
|
93
|
+
text: 'Short text',
|
|
94
|
+
email: 'Email',
|
|
95
|
+
tel: 'Phone',
|
|
96
|
+
number: 'Number',
|
|
97
|
+
date: 'Date',
|
|
98
|
+
textarea: 'Long text',
|
|
99
|
+
select: 'Dropdown',
|
|
100
|
+
checkbox: 'Checkbox'
|
|
101
|
+
},
|
|
102
|
+
half: true
|
|
103
|
+
},
|
|
104
|
+
{ key: 'required', label: 'Required', type: 'boolean' },
|
|
105
|
+
{ key: 'placeholder', label: 'Placeholder', type: 'text' },
|
|
106
|
+
{
|
|
107
|
+
key: 'options',
|
|
108
|
+
label: 'Dropdown choices',
|
|
109
|
+
type: 'strings',
|
|
110
|
+
placeholder: 'One per line'
|
|
111
|
+
}
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
});
|
|
116
|
+
/** What a template renders: every field, in order. */
|
|
117
|
+
export const formFieldsView = (value) => value.items;
|
|
@@ -27,5 +27,9 @@ export { cmsPricing, pricingView, formatPrice } from './pricing.js';
|
|
|
27
27
|
export type { CmsPricingValue, PricingFeature, PricingTier, PricingTierView } from './pricing.js';
|
|
28
28
|
export { cmsFaq, faqView, toFaqPage } from './faq.js';
|
|
29
29
|
export type { CmsFaqValue, FaqItem } from './faq.js';
|
|
30
|
+
export { cmsFormFields, formFieldsView, fieldNameFromLabel, FORM_FIELD_TYPES } from './form-fields.js';
|
|
31
|
+
export type { CmsFormFieldsValue, CmsFormFieldItem, CmsFormFieldType } from './form-fields.js';
|
|
32
|
+
export { cmsWhatsApp } from './whatsapp.js';
|
|
33
|
+
export type { CmsWhatsAppValue } from './whatsapp.js';
|
|
30
34
|
export { cmsStats, cmsSteps, cmsTimeline, cmsGallery, cmsLogos, cmsSchedule, logosView, scheduleByDay } from './presets.js';
|
|
31
35
|
export type { CmsStatsValue, StatItem, CmsStepsValue, StepItem, CmsTimelineValue, TimelineItem, CmsGalleryValue, GalleryItem, CmsLogosValue, LogoItem, LogoItemView, CmsScheduleValue, ScheduleItem } from './presets.js';
|
|
@@ -10,4 +10,6 @@ export { cmsTestimonials, testimonialsView, toReview } from './testimonials.js';
|
|
|
10
10
|
export { cmsCollection, collectionView, collectionTags, formatCollectionPrice, toItemList } from './collection.js';
|
|
11
11
|
export { cmsPricing, pricingView, formatPrice } from './pricing.js';
|
|
12
12
|
export { cmsFaq, faqView, toFaqPage } from './faq.js';
|
|
13
|
+
export { cmsFormFields, formFieldsView, fieldNameFromLabel, FORM_FIELD_TYPES } from './form-fields.js';
|
|
14
|
+
export { cmsWhatsApp } from './whatsapp.js';
|
|
13
15
|
export { cmsStats, cmsSteps, cmsTimeline, cmsGallery, cmsLogos, cmsSchedule, logosView, scheduleByDay } from './presets.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type CmsWhatsAppValue = {
|
|
2
|
+
v: 1;
|
|
3
|
+
/** International format; anything but digits is dropped when linking. */
|
|
4
|
+
number: string;
|
|
5
|
+
/** The message, with `{field}` placeholders for the visitor's answers. */
|
|
6
|
+
message: string;
|
|
7
|
+
};
|
|
8
|
+
export declare const cmsWhatsApp: import("./form.js").FormShape<CmsWhatsAppValue>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/** `CmsWhatsAppForm`: the number a form's answers go to, and how they read. */
|
|
2
|
+
import { asString, isPlainObject } from '../structured.js';
|
|
3
|
+
import { defineForm } from './form.js';
|
|
4
|
+
export const cmsWhatsApp = defineForm({
|
|
5
|
+
component: 'CmsWhatsAppForm',
|
|
6
|
+
label: 'WhatsApp',
|
|
7
|
+
version: 1,
|
|
8
|
+
translatable: ['message'],
|
|
9
|
+
normalize: (raw) => {
|
|
10
|
+
const r = isPlainObject(raw) ? raw : {};
|
|
11
|
+
return { v: 1, number: asString(r.number), message: asString(r.message) };
|
|
12
|
+
},
|
|
13
|
+
empty: () => ({ v: 1, number: '', message: '' }),
|
|
14
|
+
fields: [
|
|
15
|
+
{
|
|
16
|
+
key: 'number',
|
|
17
|
+
label: 'WhatsApp number',
|
|
18
|
+
type: 'text',
|
|
19
|
+
placeholder: '+1 555 010 0199'
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
key: 'message',
|
|
23
|
+
label: 'Message',
|
|
24
|
+
type: 'long-string',
|
|
25
|
+
placeholder: "Hi, I'm {name}…"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -31,6 +31,15 @@ export { default as CmsTimeline } from './components/cms/cms-timeline.svelte';
|
|
|
31
31
|
export { default as CmsGallery } from './components/cms/cms-gallery.svelte';
|
|
32
32
|
export { default as CmsLogos } from './components/cms/cms-logos.svelte';
|
|
33
33
|
export { default as CmsSchedule } from './components/cms/cms-schedule.svelte';
|
|
34
|
+
export { default as CmsForm } from './components/cms/cms-form.svelte';
|
|
35
|
+
export type { CmsFormState, CmsFormStatus } from './components/cms/cms-form.svelte';
|
|
36
|
+
export { default as CmsFormFields } from './components/cms/cms-form-fields.svelte';
|
|
37
|
+
export { default as CmsWhatsAppForm } from './components/cms/cms-whatsapp-form.svelte';
|
|
38
|
+
export type { CmsWhatsAppFormState } from './components/cms/cms-whatsapp-form.svelte';
|
|
39
|
+
export { default as CmsWhatsAppCart } from './components/cms/cms-whatsapp-cart.svelte';
|
|
40
|
+
export type { CmsWhatsAppCartState } from './components/cms/cms-whatsapp-cart.svelte';
|
|
41
|
+
export { whatsappCart } from './components/cms/whatsapp-cart.svelte.js';
|
|
42
|
+
export type { WhatsAppCartItem, WhatsAppPlacedOrder } from './components/cms/whatsapp-cart.svelte.js';
|
|
34
43
|
export * from './core/shapes/index.js';
|
|
35
44
|
export { default as AdminBar } from './components/admin-bar/admin-bar.svelte';
|
|
36
45
|
export { useCmsField, resolveScopeRef, ROOT_SCOPE_ID } from './components/cms/use-cms-field.svelte.js';
|
package/dist/index.js
CHANGED
|
@@ -33,6 +33,14 @@ export { default as CmsTimeline } from './components/cms/cms-timeline.svelte';
|
|
|
33
33
|
export { default as CmsGallery } from './components/cms/cms-gallery.svelte';
|
|
34
34
|
export { default as CmsLogos } from './components/cms/cms-logos.svelte';
|
|
35
35
|
export { default as CmsSchedule } from './components/cms/cms-schedule.svelte';
|
|
36
|
+
// Forms: post to a hosted form by `key` or the template's own `action`.
|
|
37
|
+
// The inputs are the template's children, or a `CmsFormFields` block when
|
|
38
|
+
// editors should control them.
|
|
39
|
+
export { default as CmsForm } from './components/cms/cms-form.svelte';
|
|
40
|
+
export { default as CmsFormFields } from './components/cms/cms-form-fields.svelte';
|
|
41
|
+
export { default as CmsWhatsAppForm } from './components/cms/cms-whatsapp-form.svelte';
|
|
42
|
+
export { default as CmsWhatsAppCart } from './components/cms/cms-whatsapp-cart.svelte';
|
|
43
|
+
export { whatsappCart } from './components/cms/whatsapp-cart.svelte.js';
|
|
36
44
|
export * from './core/shapes/index.js';
|
|
37
45
|
// Admin bar (sync wrapper; admin-bar-internal + seo-panel are async chunks)
|
|
38
46
|
export { default as AdminBar } from './components/admin-bar/admin-bar.svelte';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velastack/cms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "A scope-aware CMS for SvelteKit, for static or dynamic sites.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|
|
@@ -126,6 +126,7 @@
|
|
|
126
126
|
"@tiptap/extension-link": "^3.30.6",
|
|
127
127
|
"@tiptap/pm": "^3.30.6",
|
|
128
128
|
"@tiptap/starter-kit": "^3.30.6",
|
|
129
|
+
"@velastack/whatsapp": "^0.1.0",
|
|
129
130
|
"bits-ui": "^2.19.0",
|
|
130
131
|
"devalue": "^6.0.0",
|
|
131
132
|
"tailwind-merge": "^3.6.0"
|