@velastack/cms 0.7.0 → 0.8.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 +5 -2
- package/dist/components/cms/cms-collection.svelte +5 -1
- package/dist/components/cms/cms-pricing.svelte +7 -3
- package/dist/components/cms/cms-whatsapp-cart.svelte +17 -8
- package/dist/components/cms/cms-whatsapp-cart.svelte.d.ts +6 -1
- package/dist/core/shapes/collection.d.ts +11 -3
- package/dist/core/shapes/collection.js +13 -12
- package/dist/core/shapes/pricing.d.ts +6 -3
- package/dist/core/shapes/pricing.js +21 -9
- package/dist/core/site-currency.d.ts +29 -0
- package/dist/core/site-currency.js +60 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -317,6 +317,8 @@ Hours, navigation, a team, a price list: one value with a fixed shape, edited in
|
|
|
317
317
|
|
|
318
318
|
Templates use only these exports; there is no open item schema. A shape outside the presets is a new preset in the package. `fallback` takes the full value or a bare item list and is normalized like a stored value, so a hand-written seed with missing fields is safe. Prices, times, dates and day names format in the visitor's locale; `isOpenNow` is computed when the page renders, so a prerendered page shows the state at build time.
|
|
319
319
|
|
|
320
|
+
**Prices are in the site's currency, formatted for the business's country.** A business has one currency, the one it prices in and is paid in, declared as a `currency` field in a Site Options group (see [Site options](#site-options)), and one country (`country`, ISO 3166-1 alpha-2). `CmsCollection` fills the currency into each item's `price`, and `CmsPricing` uses it when the block sets none, so a template formats `item.price` or reads `priceText` without knowing about it; `siteCurrency(cms.site)` reads it for anything else. Format with `siteLocale(cms.site, locale)`, the page's language in the business's country: a bare `es` shows MXN as `360,00 MXN`, `es-MX` as `$360.00`. There is no per-item currency in the editor; an item written with one before this is still read as written.
|
|
321
|
+
|
|
320
322
|
Every value type (`CmsHoursValue`, `NavItem`, …), schema (`cmsHours`, …) and JSON-LD helper is exported from the package root.
|
|
321
323
|
|
|
322
324
|
### Building one
|
|
@@ -498,7 +500,8 @@ A cart whose checkout is a WhatsApp chat. Visitors add items with `whatsappCart.
|
|
|
498
500
|
</CmsWhatsAppCart>
|
|
499
501
|
```
|
|
500
502
|
|
|
501
|
-
- **One currency, the business's own.** Every price is in it, and the order and message are too. It comes from the `currency`
|
|
503
|
+
- **One currency, the business's own.** Every price is in it, and the order and message are too. It comes from the `currency` Site Option (a `currency` field in any group of `createCms({ site })`), or the `currency` prop. `formatPrice` formats an amount in the page's language in the business's country (`siteLocale`), so on a Mexican site an English page shows MXN as `MX$5.50` and a Spanish one as `$5.50`.
|
|
504
|
+
- **The number** is the field's own setting, or the `number` prop when the template already keeps it in its contact details (`<CmsContact>`'s `whatsapp`), so an editor enters it once.
|
|
502
505
|
- **Prices are optional**, and written as an editor types them (`4.5`). `cart.subtotal` exists only when every item has a price; otherwise the business quotes the total in the chat.
|
|
503
506
|
- **The message is the editor's**, set from the pencil like `CmsWhatsAppForm`'s, with `{ref}`, `{items}`, `{total}`, `{name}`, `{phone}` and `{note}`. Until one is set, a default lists the items, the total and the customer. Lines an empty placeholder leaves blank are dropped.
|
|
504
507
|
- **After checkout**, `placed` holds the order's reference and chat link until `cart.clear()`. Adding an item starts the next order.
|
|
@@ -653,7 +656,7 @@ editable page metadata (cms.metadata)
|
|
|
653
656
|
|
|
654
657
|
### Site options
|
|
655
658
|
|
|
656
|
-
`createCms({ site })` declares the **Site Options** panel: project-wide values that never render on the page — the schema.org type (`enum`), the default share image (`image`), which locales are enabled (`boolean`). Field types are `text`, `markdown`, `number`, `datetime`, `url`, `color`, `image`, `enum` (with `values`) and `boolean`. Anything a visitor sees belongs in the root layout scope, edited in place with `scope="root"`; the site tree is one non-localised tree per project.
|
|
659
|
+
`createCms({ site })` declares the **Site Options** panel: project-wide values that never render on the page — the schema.org type (`enum`), the default share image (`image`), which locales are enabled (`boolean`), the business's currency and country (`text`, ISO 4217 and ISO 3166-1 alpha-2, in a group such as `business`: `siteCurrency`, `siteCountry` and `siteLocale` find a `currency` or `country` field in any group). Field types are `text`, `markdown`, `number`, `datetime`, `url`, `color`, `image`, `enum` (with `values`) and `boolean`. Anything a visitor sees belongs in the root layout scope, edited in place with `scope="root"`; the site tree is one non-localised tree per project.
|
|
657
660
|
|
|
658
661
|
## Adapters
|
|
659
662
|
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
<script lang="ts">
|
|
11
11
|
import type { Snippet } from 'svelte';
|
|
12
12
|
import Structured from './cms-structured.svelte';
|
|
13
|
+
import { cms } from './cms-store.svelte.js';
|
|
14
|
+
import { siteCurrency } from '../../core/site-currency.js';
|
|
13
15
|
import {
|
|
14
16
|
cmsCollection,
|
|
15
17
|
collectionView,
|
|
@@ -33,6 +35,8 @@
|
|
|
33
35
|
children: Snippet<[CollectionItemView[]]>;
|
|
34
36
|
};
|
|
35
37
|
let { name, scope, fallback, value, limit, featured, tag, children }: Props = $props();
|
|
38
|
+
// Prices are in the site's currency unless an item still carries its own.
|
|
39
|
+
const currency = $derived(siteCurrency(cms.site));
|
|
36
40
|
</script>
|
|
37
41
|
|
|
38
42
|
<Structured
|
|
@@ -41,6 +45,6 @@
|
|
|
41
45
|
{scope}
|
|
42
46
|
{fallback}
|
|
43
47
|
{value}
|
|
44
|
-
view={(v: CmsCollectionValue) => collectionView(v, { limit, featured, tag })}
|
|
48
|
+
view={(v: CmsCollectionValue) => collectionView(v, { limit, featured, tag }, currency)}
|
|
45
49
|
{children}
|
|
46
50
|
/>
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
<script lang="ts">
|
|
7
7
|
import type { Snippet } from 'svelte';
|
|
8
8
|
import Structured from './cms-structured.svelte';
|
|
9
|
-
import { cmsStore } from './cms-store.svelte.js';
|
|
9
|
+
import { cms, cmsStore } from './cms-store.svelte.js';
|
|
10
|
+
import { siteCurrency, siteLocale } from '../../core/site-currency.js';
|
|
10
11
|
import {
|
|
11
12
|
cmsPricing,
|
|
12
13
|
pricingView,
|
|
@@ -22,7 +23,10 @@
|
|
|
22
23
|
children: Snippet<[PricingTierView[]]>;
|
|
23
24
|
};
|
|
24
25
|
let { name, scope, fallback, value, children }: Props = $props();
|
|
25
|
-
|
|
26
|
+
// The page's language in the business's country, and the site's currency
|
|
27
|
+
// unless the block sets one of its own.
|
|
28
|
+
const locale = $derived(siteLocale(cms.site, cmsStore.activeLocale || 'en') ?? 'en');
|
|
29
|
+
const currency = $derived(siteCurrency(cms.site));
|
|
26
30
|
</script>
|
|
27
31
|
|
|
28
32
|
<Structured
|
|
@@ -31,6 +35,6 @@
|
|
|
31
35
|
{scope}
|
|
32
36
|
{fallback}
|
|
33
37
|
{value}
|
|
34
|
-
view={(v: CmsPricingValue) => pricingView(v, locale)}
|
|
38
|
+
view={(v: CmsPricingValue) => pricingView(v, locale, currency)}
|
|
35
39
|
{children}
|
|
36
40
|
/>
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
import Structured from './cms-structured.svelte';
|
|
46
46
|
import FormControls from './cms-form-controls.svelte';
|
|
47
47
|
import { cms, cmsStore } from './cms-store.svelte.js';
|
|
48
|
+
import { siteCurrency, siteLocale } from '../../core/site-currency.js';
|
|
48
49
|
import { useFormTarget, type FormStatus } from './use-form-target.svelte.js';
|
|
49
50
|
import { setWhatsAppFieldNames } from './cms-whatsapp-form.svelte';
|
|
50
51
|
import { cmsWhatsApp, type CmsWhatsAppValue } from '../../core/shapes/whatsapp.js';
|
|
@@ -66,9 +67,14 @@
|
|
|
66
67
|
action?: string;
|
|
67
68
|
/**
|
|
68
69
|
* ISO 4217: the business's currency, which every price is in. Defaults
|
|
69
|
-
* to the `currency`
|
|
70
|
+
* to the `currency` Site Option.
|
|
70
71
|
*/
|
|
71
72
|
currency?: string;
|
|
73
|
+
/**
|
|
74
|
+
* The business's WhatsApp number, when the template keeps it in its
|
|
75
|
+
* contact details rather than in this field's settings.
|
|
76
|
+
*/
|
|
77
|
+
number?: string;
|
|
72
78
|
children: Snippet<[CmsWhatsAppCartState]>;
|
|
73
79
|
};
|
|
74
80
|
let {
|
|
@@ -79,20 +85,23 @@
|
|
|
79
85
|
key,
|
|
80
86
|
action,
|
|
81
87
|
currency,
|
|
88
|
+
number,
|
|
82
89
|
children: content,
|
|
83
90
|
...rest
|
|
84
91
|
}: Props = $props();
|
|
85
92
|
|
|
86
93
|
$effect.pre(() => {
|
|
87
|
-
|
|
88
|
-
whatsappCart.currency =
|
|
89
|
-
currency ?? (typeof fromSite === 'string' ? fromSite.trim().toUpperCase() : '');
|
|
94
|
+
whatsappCart.currency = currency?.trim().toUpperCase() || siteCurrency(cms.site) || '';
|
|
90
95
|
});
|
|
91
96
|
|
|
92
|
-
// The page's language
|
|
93
|
-
//
|
|
97
|
+
// The page's language in the business's country, not the browser's
|
|
98
|
+
// settings: on a Mexican site an English page shows MXN as `MX$5.50` and a
|
|
99
|
+
// Spanish one as `$5.50`.
|
|
94
100
|
const pageLocale = () =>
|
|
95
|
-
(
|
|
101
|
+
siteLocale(
|
|
102
|
+
cms.site,
|
|
103
|
+
(typeof document !== 'undefined' && document.documentElement.lang) || undefined
|
|
104
|
+
);
|
|
96
105
|
|
|
97
106
|
const target = useFormTarget(() => ({ key, action }));
|
|
98
107
|
setWhatsAppFieldNames(() => ORDER_PLACEHOLDERS);
|
|
@@ -172,7 +181,7 @@
|
|
|
172
181
|
const text = tidyMessage(
|
|
173
182
|
fillMessage(settings.message || DEFAULT_ORDER_MESSAGE, orderPlaceholders(order, pageLocale()))
|
|
174
183
|
);
|
|
175
|
-
const href = whatsappHref(settings.number, text);
|
|
184
|
+
const href = whatsappHref(number || settings.number, text);
|
|
176
185
|
if (!href) {
|
|
177
186
|
status = 'error';
|
|
178
187
|
message = 'No WhatsApp number is set.';
|
|
@@ -39,9 +39,14 @@ type Props = Omit<HTMLFormAttributes, 'action' | 'method' | 'children'> & {
|
|
|
39
39
|
action?: string;
|
|
40
40
|
/**
|
|
41
41
|
* ISO 4217: the business's currency, which every price is in. Defaults
|
|
42
|
-
* to the `currency`
|
|
42
|
+
* to the `currency` Site Option.
|
|
43
43
|
*/
|
|
44
44
|
currency?: string;
|
|
45
|
+
/**
|
|
46
|
+
* The business's WhatsApp number, when the template keeps it in its
|
|
47
|
+
* contact details rather than in this field's settings.
|
|
48
|
+
*/
|
|
49
|
+
number?: string;
|
|
45
50
|
children: Snippet<[CmsWhatsAppCartState]>;
|
|
46
51
|
};
|
|
47
52
|
declare const CmsWhatsappCart: import("svelte").Component<Props, {}, "">;
|
|
@@ -14,7 +14,11 @@ export type CollectionDetail = {
|
|
|
14
14
|
};
|
|
15
15
|
export type CollectionPrice = {
|
|
16
16
|
amount: number | null;
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* ISO 4217. Normally blank: a price is in the site's currency (the
|
|
19
|
+
* `currency` Site Option), which `collectionView` fills in. Content
|
|
20
|
+
* written before that may still carry one per item; it is kept as read.
|
|
21
|
+
*/
|
|
18
22
|
currency: string;
|
|
19
23
|
/** `night`, `person`, `hour`… shown after the amount. */
|
|
20
24
|
period: string;
|
|
@@ -50,8 +54,12 @@ export type CollectionSlice = {
|
|
|
50
54
|
/** Only items carrying this tag. */
|
|
51
55
|
tag?: string;
|
|
52
56
|
};
|
|
53
|
-
/**
|
|
54
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Visible items (not hidden, with a title), optionally sliced. `currency` is
|
|
59
|
+
* the site's; a price without one of its own is in it, so templates format
|
|
60
|
+
* `item.price` as is.
|
|
61
|
+
*/
|
|
62
|
+
export declare const collectionView: (value: CmsCollectionValue, slice?: CollectionSlice, currency?: string) => CollectionItemView[];
|
|
55
63
|
/** Every tag in use, in first-seen order. */
|
|
56
64
|
export declare const collectionTags: (value: CmsCollectionValue) => string[];
|
|
57
65
|
/** `from €120 / night`, or empty when there is no amount. */
|
|
@@ -128,22 +128,19 @@ export const cmsCollection = defineForm({
|
|
|
128
128
|
placeholder: 'from',
|
|
129
129
|
group: 'Pricing'
|
|
130
130
|
},
|
|
131
|
-
|
|
132
|
-
{
|
|
133
|
-
key: 'price.currency',
|
|
134
|
-
label: 'Currency',
|
|
135
|
-
type: 'text',
|
|
136
|
-
half: true,
|
|
137
|
-
placeholder: 'EUR',
|
|
138
|
-
group: 'Pricing'
|
|
139
|
-
},
|
|
131
|
+
// In the site's currency (Site Options); there is no per-item one.
|
|
132
|
+
{ key: 'price.amount', label: 'Amount', type: 'number', half: true, group: 'Pricing' },
|
|
140
133
|
{ key: 'price.period', label: 'Per', type: 'text', placeholder: 'night', group: 'Pricing' }
|
|
141
134
|
]
|
|
142
135
|
}
|
|
143
136
|
]
|
|
144
137
|
});
|
|
145
|
-
/**
|
|
146
|
-
|
|
138
|
+
/**
|
|
139
|
+
* Visible items (not hidden, with a title), optionally sliced. `currency` is
|
|
140
|
+
* the site's; a price without one of its own is in it, so templates format
|
|
141
|
+
* `item.price` as is.
|
|
142
|
+
*/
|
|
143
|
+
export const collectionView = (value, slice = {}, currency) => {
|
|
147
144
|
let items = value.items.filter((i) => !i.hidden && i.title !== '');
|
|
148
145
|
if (slice.featured)
|
|
149
146
|
items = items.filter((i) => i.featured);
|
|
@@ -151,7 +148,11 @@ export const collectionView = (value, slice = {}) => {
|
|
|
151
148
|
items = items.filter((i) => i.tags.includes(slice.tag));
|
|
152
149
|
if (slice.limit !== undefined && slice.limit >= 0)
|
|
153
150
|
items = items.slice(0, slice.limit);
|
|
154
|
-
return items.map((i) => ({
|
|
151
|
+
return items.map((i) => ({
|
|
152
|
+
...i,
|
|
153
|
+
price: i.price.currency || !currency ? i.price : { ...i.price, currency },
|
|
154
|
+
href: i.link ? linkHref(i.link) || null : null
|
|
155
|
+
}));
|
|
155
156
|
};
|
|
156
157
|
/** Every tag in use, in first-seen order. */
|
|
157
158
|
export const collectionTags = (value) => {
|
|
@@ -22,7 +22,7 @@ export type PricingTier = {
|
|
|
22
22
|
};
|
|
23
23
|
export type CmsPricingValue = {
|
|
24
24
|
v: 1;
|
|
25
|
-
/** ISO 4217
|
|
25
|
+
/** ISO 4217. Blank means the site's currency (the `currency` Site Option). */
|
|
26
26
|
currency: string;
|
|
27
27
|
labels: {
|
|
28
28
|
custom: string;
|
|
@@ -36,5 +36,8 @@ export type PricingTierView = PricingTier & {
|
|
|
36
36
|
export declare const cmsPricing: import("./form.js").FormShape<CmsPricingValue>;
|
|
37
37
|
/** `€29 / month`, the custom label, or empty without an amount. */
|
|
38
38
|
export declare const formatPrice: (tier: PricingTier, value: Pick<CmsPricingValue, "currency" | "labels">, locale: string) => string;
|
|
39
|
-
/**
|
|
40
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Tiers with a name, each with its formatted price and CTA href. `currency`
|
|
41
|
+
* is the site's, used when the block sets none of its own.
|
|
42
|
+
*/
|
|
43
|
+
export declare const pricingView: (value: CmsPricingValue, locale: string, currency?: string) => PricingTierView[];
|
|
@@ -42,7 +42,13 @@ export const cmsPricing = defineForm({
|
|
|
42
42
|
},
|
|
43
43
|
empty: () => ({ v: 1, currency: '', labels: { custom: 'Custom' }, items: [] }),
|
|
44
44
|
fields: [
|
|
45
|
-
{
|
|
45
|
+
{
|
|
46
|
+
key: 'currency',
|
|
47
|
+
label: 'Currency',
|
|
48
|
+
type: 'text',
|
|
49
|
+
half: true,
|
|
50
|
+
placeholder: 'Blank: the site’s'
|
|
51
|
+
},
|
|
46
52
|
{ key: 'labels.custom', label: '“Custom” price label', type: 'text' },
|
|
47
53
|
{
|
|
48
54
|
key: 'items',
|
|
@@ -105,11 +111,17 @@ export const formatPrice = (tier, value, locale) => {
|
|
|
105
111
|
}
|
|
106
112
|
return tier.price.period ? `${amount} / ${tier.price.period}` : amount;
|
|
107
113
|
};
|
|
108
|
-
/**
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Tiers with a name, each with its formatted price and CTA href. `currency`
|
|
116
|
+
* is the site's, used when the block sets none of its own.
|
|
117
|
+
*/
|
|
118
|
+
export const pricingView = (value, locale, currency) => {
|
|
119
|
+
const priced = value.currency || !currency ? value : { ...value, currency };
|
|
120
|
+
return value.items
|
|
121
|
+
.filter((t) => t.name !== '')
|
|
122
|
+
.map((t) => ({
|
|
123
|
+
...t,
|
|
124
|
+
priceText: formatPrice(t, priced, locale),
|
|
125
|
+
href: t.cta ? linkHref(t.cta) || null : null
|
|
126
|
+
}));
|
|
127
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The business's currency, from Site Options.
|
|
3
|
+
*
|
|
4
|
+
* A site has one currency: the one the business prices in and is paid in
|
|
5
|
+
* (MXN for a bakery in Mexico). Templates declare it as a `currency` field in
|
|
6
|
+
* one of their Site Options groups (`business.currency`, say), and every
|
|
7
|
+
* price on the site is in it: a collection item's, a pricing tier's, the
|
|
8
|
+
* WhatsApp cart's. A visitor sees another currency only as a display
|
|
9
|
+
* conversion, which is a later concern, never as a second price.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* The ISO 4217 code a Site Options tree declares: a top-level `currency`, or
|
|
13
|
+
* a `currency` field in whichever group the template put it in. `undefined`
|
|
14
|
+
* when none is set or the value is not a three-letter code.
|
|
15
|
+
*/
|
|
16
|
+
export declare const siteCurrency: (site: Record<string, unknown> | undefined) => string | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* The ISO 3166-1 alpha-2 code of the country the business is in, from a
|
|
19
|
+
* `country` field declared like `currency`. `undefined` when none is set.
|
|
20
|
+
*/
|
|
21
|
+
export declare const siteCountry: (site: Record<string, unknown> | undefined) => string | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* The locale prices format in: the page's language, in the business's
|
|
24
|
+
* country. A bare language does not know where the business is, and that
|
|
25
|
+
* decides the format: Spanish alone shows MXN as `360,00 MXN`, Mexican
|
|
26
|
+
* Spanish (`es-MX`) as `$360.00`, which is what the bakery's customers read.
|
|
27
|
+
* A `language` that already names a region is kept as it is.
|
|
28
|
+
*/
|
|
29
|
+
export declare const siteLocale: (site: Record<string, unknown> | undefined, language: string | undefined) => string | undefined;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The business's currency, from Site Options.
|
|
3
|
+
*
|
|
4
|
+
* A site has one currency: the one the business prices in and is paid in
|
|
5
|
+
* (MXN for a bakery in Mexico). Templates declare it as a `currency` field in
|
|
6
|
+
* one of their Site Options groups (`business.currency`, say), and every
|
|
7
|
+
* price on the site is in it: a collection item's, a pricing tier's, the
|
|
8
|
+
* WhatsApp cart's. A visitor sees another currency only as a display
|
|
9
|
+
* conversion, which is a later concern, never as a second price.
|
|
10
|
+
*/
|
|
11
|
+
const CURRENCY = /^[A-Z]{3}$/;
|
|
12
|
+
const COUNTRY = /^[A-Z]{2}$/;
|
|
13
|
+
const asCode = (value, shape) => {
|
|
14
|
+
if (typeof value !== 'string')
|
|
15
|
+
return undefined;
|
|
16
|
+
const code = value.trim().toUpperCase();
|
|
17
|
+
return shape.test(code) ? code : undefined;
|
|
18
|
+
};
|
|
19
|
+
/** A `key` field at the top level or in any group, as an upper-cased code of `shape`. */
|
|
20
|
+
const siteCode = (site, key, shape) => {
|
|
21
|
+
if (!site)
|
|
22
|
+
return undefined;
|
|
23
|
+
const top = asCode(site[key], shape);
|
|
24
|
+
if (top)
|
|
25
|
+
return top;
|
|
26
|
+
for (const group of Object.values(site)) {
|
|
27
|
+
if (group && typeof group === 'object' && !Array.isArray(group)) {
|
|
28
|
+
const code = asCode(group[key], shape);
|
|
29
|
+
if (code)
|
|
30
|
+
return code;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The ISO 4217 code a Site Options tree declares: a top-level `currency`, or
|
|
37
|
+
* a `currency` field in whichever group the template put it in. `undefined`
|
|
38
|
+
* when none is set or the value is not a three-letter code.
|
|
39
|
+
*/
|
|
40
|
+
export const siteCurrency = (site) => siteCode(site, 'currency', CURRENCY);
|
|
41
|
+
/**
|
|
42
|
+
* The ISO 3166-1 alpha-2 code of the country the business is in, from a
|
|
43
|
+
* `country` field declared like `currency`. `undefined` when none is set.
|
|
44
|
+
*/
|
|
45
|
+
export const siteCountry = (site) => siteCode(site, 'country', COUNTRY);
|
|
46
|
+
/**
|
|
47
|
+
* The locale prices format in: the page's language, in the business's
|
|
48
|
+
* country. A bare language does not know where the business is, and that
|
|
49
|
+
* decides the format: Spanish alone shows MXN as `360,00 MXN`, Mexican
|
|
50
|
+
* Spanish (`es-MX`) as `$360.00`, which is what the bakery's customers read.
|
|
51
|
+
* A `language` that already names a region is kept as it is.
|
|
52
|
+
*/
|
|
53
|
+
export const siteLocale = (site, language) => {
|
|
54
|
+
const country = siteCountry(site);
|
|
55
|
+
if (!language)
|
|
56
|
+
return undefined;
|
|
57
|
+
if (!country || language.includes('-'))
|
|
58
|
+
return language;
|
|
59
|
+
return `${language}-${country}`;
|
|
60
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ export { default as CmsWhatsAppCart } from './components/cms/cms-whatsapp-cart.s
|
|
|
40
40
|
export type { CmsWhatsAppCartState } from './components/cms/cms-whatsapp-cart.svelte';
|
|
41
41
|
export { whatsappCart } from './components/cms/whatsapp-cart.svelte.js';
|
|
42
42
|
export type { WhatsAppCartItem, WhatsAppPlacedOrder } from './components/cms/whatsapp-cart.svelte.js';
|
|
43
|
+
export { siteCountry, siteCurrency, siteLocale } from './core/site-currency.js';
|
|
43
44
|
export * from './core/shapes/index.js';
|
|
44
45
|
export { default as AdminBar } from './components/admin-bar/admin-bar.svelte';
|
|
45
46
|
export { useCmsField, resolveScopeRef, ROOT_SCOPE_ID } from './components/cms/use-cms-field.svelte.js';
|
package/dist/index.js
CHANGED
|
@@ -41,6 +41,7 @@ export { default as CmsFormFields } from './components/cms/cms-form-fields.svelt
|
|
|
41
41
|
export { default as CmsWhatsAppForm } from './components/cms/cms-whatsapp-form.svelte';
|
|
42
42
|
export { default as CmsWhatsAppCart } from './components/cms/cms-whatsapp-cart.svelte';
|
|
43
43
|
export { whatsappCart } from './components/cms/whatsapp-cart.svelte.js';
|
|
44
|
+
export { siteCountry, siteCurrency, siteLocale } from './core/site-currency.js';
|
|
44
45
|
export * from './core/shapes/index.js';
|
|
45
46
|
// Admin bar (sync wrapper; admin-bar-internal + seo-panel are async chunks)
|
|
46
47
|
export { default as AdminBar } from './components/admin-bar/admin-bar.svelte';
|