@orderlyshop/web-components 0.1.0-build.7050 → 0.1.0-build.7051
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/README.md +117 -15
- package/bin/orderly-init-shop.mjs +41 -16
- package/custom-elements.json +37 -4
- package/dist/browser/orderly-web-components.define.global.js +924 -219
- package/dist/browser/orderly-web-components.define.global.js.map +1 -1
- package/dist/browser/orderly-web-components.global.js +924 -219
- package/dist/browser/orderly-web-components.global.js.map +1 -1
- package/dist/{default-shop-DgX6uy10.d.ts → default-shop-Qipu74bd.d.ts} +51 -9
- package/dist/default-shop.d.ts +2 -2
- package/dist/default-shop.js +86 -0
- package/dist/default-shop.js.map +1 -1
- package/dist/define-jKa_4XhP.d.ts +9 -0
- package/dist/define.d.ts +1 -1
- package/dist/define.js +2116 -379
- package/dist/define.js.map +1 -1
- package/dist/index.d.ts +53 -9
- package/dist/index.js +2624 -386
- package/dist/index.js.map +1 -1
- package/dist/navigation.d.ts +5 -2
- package/dist/navigation.js +238 -38
- package/dist/navigation.js.map +1 -1
- package/dist/{shop-BgQhGRzS.d.ts → shop-Dm2UjjIN.d.ts} +6 -1
- package/dist/shop.d.ts +4 -4
- package/dist/shop.js +2655 -498
- package/dist/shop.js.map +1 -1
- package/dist/stores.d.ts +26 -3
- package/dist/stores.js +158 -1
- package/dist/stores.js.map +1 -1
- package/dist/taxonomy.d.ts +2 -2
- package/dist/types-CXEwL2xS.d.ts +170 -0
- package/docs/components/README.md +76 -5
- package/docs/components/product-grid.md +1 -2
- package/examples/shop/README.md +2 -0
- package/examples/shop/src/includes/head.html +16 -4
- package/examples/shop/src/payment-failure.html +20 -0
- package/examples/shop/src/payment-success.html +20 -0
- package/examples/shop/src/storefront-router.ts +151 -0
- package/examples/shop/src/templates/page-layouts.html +199 -1
- package/html-custom-data.json +33 -4
- package/package.json +2 -2
- package/dist/define-BNMhl19n.d.ts +0 -9
- package/dist/types-Bjez59Hr.d.ts +0 -96
package/AGENTS.md
CHANGED
|
@@ -5,6 +5,7 @@ Use this package as a headless storefront component library. Prefer native custo
|
|
|
5
5
|
## Discovery
|
|
6
6
|
|
|
7
7
|
- Read `README.md` for setup and package-wide concepts.
|
|
8
|
+
- Read `DESIGN.md` before changing the visual theme. It defines the semantic design tokens that should be changed first.
|
|
8
9
|
- Read `docs/components/README.md` for component APIs.
|
|
9
10
|
- Read `docs/components/product-rail.md` and `docs/components/product-grid.md` before implementing product listing UI.
|
|
10
11
|
- Read `custom-elements.json` for machine-readable custom element metadata.
|
|
@@ -34,6 +35,7 @@ Use this package as a headless storefront component library. Prefer native custo
|
|
|
34
35
|
## Implementation Defaults
|
|
35
36
|
|
|
36
37
|
- Keep components headless. Add behavior and default light-DOM markup, not bundled theme CSS.
|
|
38
|
+
- Start theme work from the semantic tokens in `DESIGN.md`. Use component-local selectors and hooks only for deliberate exceptions after the shared tokens are set.
|
|
37
39
|
- Built-in UI copy defaults to Danish. Use `configureShop({ uiLanguage: "EN" })` for English shops, and prefer shop attributes/templates for wording that is unique to one storefront.
|
|
38
40
|
- Preserve template override support through `data-orderly-template`, `data-orderly-bind`, `data-orderly-slot`, and `data-orderly-action`.
|
|
39
41
|
- Put shop-wide templates in a shared document-level registry, for example template files included from `body-start.html`, using `data-orderly-template` plus `data-orderly-for`.
|
package/README.md
CHANGED
|
@@ -20,13 +20,15 @@ npm install
|
|
|
20
20
|
npm run dev
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
Agents and developers should use `npx orderly-init-shop` before hand-writing shop files. It creates `src/index.html`, `src/category.html`, `src/product.html`, `src/checkout.html`, `src/includes/head.html`, `src/navigation.ts`, `src/shop-query.ts`, `src/templates/*.html`, `src/style.css`, `vite.config.mjs`, and package scripts. Pass an account id when the shop-wide default query should be tenant-scoped from the start:
|
|
23
|
+
Agents and developers should use `npx orderly-init-shop` before hand-writing shop files. It creates `src/index.html`, `src/category.html`, `src/product.html`, `src/checkout.html`, `src/payment-success.html`, `src/payment-failure.html`, `src/includes/head.html`, `src/navigation.ts`, `src/shop-query.ts`, `src/templates/*.html`, `src/style.css`, `vite.config.mjs`, and package scripts. The payment pages are physical callback URLs for payment providers and use the package `orderly-payment-success-page` and `orderly-payment-failure-page` components. Pass an account id when the shop-wide default query should be tenant-scoped from the start:
|
|
24
24
|
|
|
25
25
|
```sh
|
|
26
26
|
npx orderly-init-shop --account-id 00000000-0000-0000-0000-000000000000
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
Re-running `npx orderly-init-shop` in an existing shop is non-destructive by default. Existing scaffold files are left unchanged with warnings, while missing new scaffold files are added. Use `--force` only when you explicitly want scaffold files overwritten.
|
|
30
|
+
|
|
31
|
+
The scaffold uses Vite for local development and static builds, but Vite is added to the generated shop's `devDependencies`. Consumers should test locally from `http://localhost:61677` or `https://localhost:61677` because those are the expected local development origins for backend CORS. The generated Vite setup defaults to `http://localhost:61677`; if a local reverse proxy or TLS terminator is added, keep the same `localhost:61677` origin over HTTPS. `@orderlyshop/web-components` does not depend on Vite at runtime.
|
|
30
32
|
|
|
31
33
|
## Browser Standalone Bundle
|
|
32
34
|
|
|
@@ -66,7 +68,7 @@ Use the auto-register bundle for the built-in default shop or pages that configu
|
|
|
66
68
|
|
|
67
69
|
The normal package entrypoints remain ESM for bundlers. The standalone files are classic browser scripts and expose `window.OrderlyWebComponents`.
|
|
68
70
|
|
|
69
|
-
`configureShop()` is the recommended one-call setup for HTML head scripts. Its parameter is strongly typed as `ShopConfig`, so VS Code/TypeScript can provide completions directly on the object literal. All fields are optional: `uiLanguage`, `defaultShop`, `pageLayout`, `responsiveTemplates`, `shopFooter`, `storedImageUrls`, and `components`. It applies configuration first and then registers custom elements. Pass `components: false` only when elements are registered elsewhere.
|
|
71
|
+
`configureShop()` is the recommended one-call setup for HTML head scripts. Its parameter is strongly typed as `ShopConfig`, so VS Code/TypeScript can provide completions directly on the object literal. All fields are optional: `uiLanguage`, `defaultShop`, `storefrontRouter`, `pageLayout`, `responsiveTemplates`, `shopFooter`, `storedImageUrls`, and `components`. It applies configuration first and then registers custom elements. The built-in SPA storefront router is enabled by default; pass `storefrontRouter: { enabled: false }` only when a shop needs traditional full-page navigation for every internal link. Pass `components: false` only when elements are registered elsewhere.
|
|
70
72
|
|
|
71
73
|
Built-in component copy is Danish by default (`uiLanguage: "DA"`). Set `uiLanguage: "EN"` to switch built-in labels, empty states, checkout copy, navigation controls, and page defaults to English. Shop-specific attributes, templates, and `defaultShop` label fields still take precedence over language defaults.
|
|
72
74
|
|
|
@@ -74,6 +76,7 @@ Built-in component copy is Danish by default (`uiLanguage: "DA"`). Set `uiLangua
|
|
|
74
76
|
|
|
75
77
|
Developers and coding agents should start with these package-owned docs:
|
|
76
78
|
|
|
79
|
+
- [`DESIGN.md`](./DESIGN.md) defines the package-wide design tokens and the recommended theming workflow for consistent styling across all components.
|
|
77
80
|
- [`docs/components/README.md`](./docs/components/README.md) lists every custom element, common attributes, typed properties, events, and template hooks.
|
|
78
81
|
- [`docs/components/product-grid.md`](./docs/components/product-grid.md) documents search loading, sorting, paging, default query merging, and declarative query markup.
|
|
79
82
|
- [`docs/components/product-rail.md`](./docs/components/product-rail.md) documents homepage/category rails and their query configuration.
|
|
@@ -118,6 +121,82 @@ import "@orderlyshop/web-components/define";
|
|
|
118
121
|
|
|
119
122
|
The default backend is `https://service.orderly.shop`. Category links use real `/categories/<category-path>/` URLs by default so production builds can ship server-rendered category HTML. Use `categoryUrlMode: "hash"` only when a shop explicitly wants the older `/category.html#id=<category>` routing.
|
|
120
123
|
|
|
124
|
+
## SPA Storefront Navigation
|
|
125
|
+
|
|
126
|
+
SPA-style storefront navigation is the default when a shop calls `configureShop()`. It is implemented as progressive enhancement, not as a replacement for normal URLs:
|
|
127
|
+
|
|
128
|
+
- Links remain normal same-origin `<a href>` links, so Google, no-JS browsers, new tabs, copied links, and server-rendered entry pages still work.
|
|
129
|
+
- Ordinary left-click navigation between known storefront routes is intercepted in the browser and rendered without a physical reload.
|
|
130
|
+
- The router uses `history.pushState`, `popstate`, scroll restoration, canonical URLs, meta description updates, document title updates, and optional View Transitions.
|
|
131
|
+
- A shared `BasketController` is kept alive across home, category, product, checkout, payment result, and configured static routes.
|
|
132
|
+
- Unknown routes, external links, modified clicks, downloads, `_blank` links, and hash-only links fall back to the browser.
|
|
133
|
+
|
|
134
|
+
The default route coverage is:
|
|
135
|
+
|
|
136
|
+
- home: `defaultShop.homeHref`, default `/`
|
|
137
|
+
- categories: real category paths generated from `navigationDefinitions`, default `/categories/<category-path>/`
|
|
138
|
+
- product details: either hash or path product URLs, controlled by `defaultShop.productUrlMode`
|
|
139
|
+
- checkout: `defaultShop.checkoutHref`, default `/checkout.html`
|
|
140
|
+
- payment result pages: `/payment-success.html?orderid=...` and `/payment-failure.html?orderid=...`
|
|
141
|
+
- static pages: routes explicitly supplied in `storefrontRouter.staticRoutes`
|
|
142
|
+
|
|
143
|
+
The simplest setup needs no router block:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
configureShop({
|
|
147
|
+
defaultShop: {
|
|
148
|
+
homeHref: "/",
|
|
149
|
+
productHref: "/products/",
|
|
150
|
+
productUrlMode: "path",
|
|
151
|
+
productPathRoot: "/products/",
|
|
152
|
+
checkoutHref: "/checkout.html",
|
|
153
|
+
navigationDefinitions
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Use `storefrontRouter` only when the shop needs custom static pages, custom page factories, View Transition control, or an explicit opt-out:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
configureShop({
|
|
162
|
+
defaultShop: {
|
|
163
|
+
homeHref: "/",
|
|
164
|
+
productHref: "/products/",
|
|
165
|
+
productUrlMode: "path",
|
|
166
|
+
productPathRoot: "/products/",
|
|
167
|
+
checkoutHref: "/checkout.html"
|
|
168
|
+
},
|
|
169
|
+
storefrontRouter: {
|
|
170
|
+
product: {
|
|
171
|
+
mode: "path",
|
|
172
|
+
pathRoot: "/products/"
|
|
173
|
+
},
|
|
174
|
+
staticRoutes: [
|
|
175
|
+
{
|
|
176
|
+
path: "/forretningsbetingelser.html",
|
|
177
|
+
title: "Forretningsbetingelser | Example Shop",
|
|
178
|
+
description: "Køb, levering, betaling, retur og reklamation.",
|
|
179
|
+
create: () => document.querySelector("#termsPage")!.cloneNode(true) as HTMLElement
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Disable SPA navigation explicitly when a host application owns routing or when every internal link must perform a full document navigation:
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
configureShop({
|
|
190
|
+
storefrontRouter: {
|
|
191
|
+
enabled: false
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
If a shop still uses hash-based product URLs, set `productUrlMode: "hash"` or `storefrontRouter.product.mode: "hash"`. The package supports both `#url=<encoded-share-url>` and real product paths; path URLs are preferred for SEO, while hash URLs remain a fallback for shops that cannot add server-side product routing yet.
|
|
197
|
+
|
|
198
|
+
For SEO, keep physical pages or static generated pages available for every URL the router can render dynamically. The router improves navigation after the first load; it does not remove the need for server/static support for direct requests to category, product, checkout, and content URLs.
|
|
199
|
+
|
|
121
200
|
Customize the default once per shop from the HTML head:
|
|
122
201
|
|
|
123
202
|
```ts
|
|
@@ -194,13 +273,15 @@ After installing from the repository workspace, run it with:
|
|
|
194
273
|
npm run dev --workspace @orderlyshop/example-shop
|
|
195
274
|
```
|
|
196
275
|
|
|
276
|
+
Open the local shop from `http://localhost:61677` during normal development, or `https://localhost:61677` when a local TLS proxy is in front of the dev server. Consumers should keep that exact localhost origin because the backend CORS development policy expects it.
|
|
277
|
+
|
|
197
278
|
Inside an installed package, inspect the same reference implementation at:
|
|
198
279
|
|
|
199
280
|
```text
|
|
200
281
|
node_modules/@orderlyshop/web-components/examples/shop
|
|
201
282
|
```
|
|
202
283
|
|
|
203
|
-
The example demonstrates how to customize the package default with Danish navigation, shop copy,
|
|
284
|
+
The example demonstrates how to customize the package default with Danish navigation, shop copy, declarative home/category/product/checkout pages, and the default SPA router. Its router config only adds static content routes and page factories; `enabled: true` is not required. It intentionally avoids local CSS and templates while the package default look and feel is developed. Shared component configuration lives in `src/includes/head.html`; the strongly typed navigation data lives in `src/navigation.ts`. It uses the package-level `orderly-generate-category-pages` and `orderly-build-category-pages` commands for simple real-URL category pages. It is included as readable source code and is not exported as part of the runtime API.
|
|
204
285
|
|
|
205
286
|
## Navigation Setup
|
|
206
287
|
|
|
@@ -467,27 +548,49 @@ collection.query = searchQuery;
|
|
|
467
548
|
## Components
|
|
468
549
|
|
|
469
550
|
- `orderly-page-layout` provides reusable page regions for header, header actions, left/right sidebars, content, footer, and overlay content. It includes a default logo image using `https://orderly.shop/home/App_Icon.svg`; override it with `logo-src`, `logo-alt`, and `logo-href`.
|
|
470
|
-
- `orderly-home-page` composes page layout, side navigation, one product rail per top-level category, basket icon, basket drawer, product detail dialog, and footer. Configure it with `configureShop({ defaultShop })` plus attributes such as `title`, `eyebrow`, `rail-cta-label`, and `product-href`. The product dialog
|
|
471
|
-
- `orderly-category-page` composes page layout, navigation, search, product grid, product detail, basket icon, and basket around a navigation category. Without properties it uses the package default shop configuration. Override with `configureShop({ defaultShop })` once per shop, set `base-url` or `product-href` in markup, or assign `category`, `navigationItems`, `sortOptions`, `client`, `defaultQuery`, and `basketController` from JavaScript for advanced cases. The product dialog
|
|
551
|
+
- `orderly-home-page` composes page layout, side navigation, one product rail per top-level category, basket icon, basket drawer, product detail dialog, and footer. Configure it with `configureShop({ defaultShop })` plus attributes such as `title`, `eyebrow`, `rail-cta-label`, and `product-href`. The product dialog expand link follows the configured product URL mode, and compact `https://orderly.shop/...` product URLs are preserved when it builds hash or path routes.
|
|
552
|
+
- `orderly-category-page` composes page layout, navigation, search, product grid, product detail, basket icon, and basket around a navigation category. Without properties it uses the package default shop configuration. Override with `configureShop({ defaultShop })` once per shop, set `base-url` or `product-href` in markup, or assign `category`, `navigationItems`, `sortOptions`, `client`, `defaultQuery`, and `basketController` from JavaScript for advanced cases. The product dialog expand link uses the configured product URL mode and still supports explicit per-page `product-href` overrides when a shop wants a different physical product page URL.
|
|
472
553
|
- `orderly-checkout-page` composes page layout, side navigation, checkout form, basket order summary, footer, shared basket state, delivery loading, and order-created cleanup. Delivery methods render as radio cards after address entry, service points render as radio-card pickup choices when required, and delivery changes verify the draft so totals update. Configure copy through `configureShop({ defaultShop: { checkoutPageTitle, checkoutPageDescription, checkoutOrderTitle, checkoutTermsHref, checkoutLabels, basketLabels } })`, or assign `client`, `basketController`, `navigationItems`, `checkoutLabels`, and `basketLabels` from JavaScript.
|
|
473
554
|
- `orderly-product-detail-page` composes page layout, navigation, product detail, basket drawer, and footer for a product route. Set `share-url`, the `shareUrl` property, or pass `#url=<encoded-share-url>` in the page URL; compact Orderly slugs are restored to `https://orderly.shop/...` before it calls `SearchService.Search` with `SearchQuery.ShareUrl` and renders the resolved `SearchObject`.
|
|
474
555
|
- `orderly-stored-image` accepts `image: StoredImage`, resolves image URLs from the configured prefix, and applies `RotationDeg`, crop, and full-size metadata in the browser.
|
|
475
556
|
- `orderly-credit` accepts `credit: Credit` or declarative `amount` and `currency` attributes, then renders stylable money markup. DKK values are formatted as `kr. <pris>`, for example `kr. 100,00`.
|
|
476
|
-
- `orderly-product-tile` accepts `product: SearchObject` and emits `orderly-product-selected`, `orderly-add-to-basket`, and `orderly-remove-from-basket`. Templates can bind the product share URL with `data-orderly-bind="share-url"`; anchors
|
|
557
|
+
- `orderly-product-tile` accepts `product: SearchObject` and emits `orderly-product-selected`, `orderly-add-to-basket`, and `orderly-remove-from-basket`. Templates can bind the product share URL with `data-orderly-bind="share-url"`; anchors without an existing `href` keep the raw share URL, while anchors that already point at a product page are rewritten to the configured physical product route using hash or path mode. Default tiles and statically hydrated category tiles include `schema.org/Product` and `schema.org/Offer` microdata for name, image, URL, description, SKU, brand, price, currency, and in-stock availability when those fields are present. When a `BasketController` is assigned through `basketController`, the default action button toggles between add and remove, using an icon-only button next to the price.
|
|
477
558
|
- `orderly-product-page` accepts `product: SearchObject`, renders all product image thumbnails, switches the selected image on thumbnail click, and exposes product details plus add-to-basket behavior.
|
|
478
559
|
- `orderly-search-box` binds to a target `orderly-product-grid`. Use `mode="textbox"` for an inline search input or `mode="icon"` for a header icon that opens a full-page search overlay. Icon mode searches automatically with a 500ms debounce and renders matching product tiles below the search field. Default home and category pages use icon mode next to the basket icon.
|
|
479
560
|
- `orderly-product-grid` accepts `query: SearchQuery`, merges the configured shop query scope, calls `SearchService.Search`, renders its own sort control from `sortOptions`, supports opaque `ContinuationToken` paging, manual paging, dynamic/infinite scroll, and default loading placeholders while the first page is pending.
|
|
480
561
|
- `orderly-product-rail` accepts `query: SearchQuery`, title, CTA label, and CTA href, then renders the search as a horizontal scroll list by composing `orderly-product-grid`. It is useful for homepages and editorial rows where several category searches should be stacked vertically.
|
|
481
562
|
- `orderly-collection-page` accepts `query: SearchQuery`, title, description, and hero image, then delegates fetching to `orderly-product-grid`.
|
|
482
563
|
- `orderly-shop-footer` renders configurable logo, about text, address, contact information, opening hours, and information links.
|
|
483
|
-
- `orderly-basket-icon` and `orderly-basket` share a `BasketController` backed by `DraftOrder` persistence. Basket state events such as `orderly-basket-open`, `orderly-basket-change`, and `orderly-basket-verified` use the current `DraftOrder` directly as `event.detail`, so consumers can derive counts and totals from Core contracts instead of an internal basket data shape. `orderly-basket` renders an item overview, verifies
|
|
484
|
-
- `orderly-checkout` persists checkout
|
|
485
|
-
- `orderly-navigation` uses `NavigationController` and can be subclassed with site-specific `NavigationItem[]`. It is sticky by default, including when placed in `left`, `right`, or `primary-nav`; set `sticky="false"` to opt out.
|
|
564
|
+
- `orderly-basket-icon` and `orderly-basket` share a `BasketController` backed by `DraftOrder` persistence. Basket state events such as `orderly-basket-open`, `orderly-basket-change`, and `orderly-basket-verified` use the current `DraftOrder` directly as `event.detail`, so consumers can derive counts and totals from Core contracts instead of an internal basket data shape. `orderly-basket` renders an item overview, verifies non-empty persisted drafts on load plus later basket mutations through `OrderService.VerifyDraft` when a client or `base-url` is configured, includes a configurable checkout link through `checkout-href` and `checkout-label`, and only exposes quantity selection for lines where `MaxQuantity > 1`. Summary rows now render only backend-provided `DraftOrder` values, never local subtotal/total math, and the component renders both `DraftOrder.Errors` and `DraftOrderLine.Errors`. Custom basket templates can project order-level errors through `data-orderly-slot="errors"`.
|
|
565
|
+
- `orderly-checkout` persists checkout contact and address fields directly on `DraftOrder.Transport`, defaults the phone country picker to `+45`, validates email and phone values with built-in component logic, looks up Danish city names from the entered postal code through Dataforsyningen's `https://api.dataforsyningen.dk/postnumre/{postnr}` endpoint, loads delivery methods and service points, verifies the draft, and calls `OrderService.Create`.
|
|
566
|
+
- `orderly-navigation` uses `NavigationController` and can be subclassed with site-specific `NavigationItem[]`. It is sticky by default, including when placed in `left`, `right`, or `primary-nav`; set `sticky="false"` to opt out. Use `layout="vertical"` for disclosure side navigation, `layout="horizontal"` for the tiered desktop bar, or `layout="burgermenu"` for an icon trigger that expands into the same nested disclosure menu inside a floating panel.
|
|
486
567
|
- `orderly-search-box`, `orderly-sort-select`, `orderly-filter-panel`, and `orderly-load-more` bind to `orderly-product-grid`.
|
|
487
568
|
|
|
488
569
|
## Rendering And Styling
|
|
489
570
|
|
|
490
|
-
The package ships baseline default CSS and uses light DOM. Default markup uses stable `orderly-*` class names and CSS variables
|
|
571
|
+
The package ships baseline default CSS and uses light DOM. Default markup uses stable `orderly-*` class names and CSS variables, so host CSS can style or replace the default look directly.
|
|
572
|
+
|
|
573
|
+
For consistent theming across the whole package, start with the semantic design contract in [`DESIGN.md`](./DESIGN.md). In practice that means overriding `--orderly-color-primary`, `--orderly-color-primary-soft`, `--orderly-color-primary-contrast`, the `--orderly-action-primary-*` and `--orderly-action-secondary-*` tokens, plus shared tokens such as `--orderly-link-accent-color`, `--orderly-selection-*`, and `--orderly-badge-*`. `--orderly-color-accent` is kept as a compatibility alias, but new themes should treat `--orderly-color-primary` as the source of truth.
|
|
574
|
+
|
|
575
|
+
Example:
|
|
576
|
+
|
|
577
|
+
```css
|
|
578
|
+
:root {
|
|
579
|
+
--orderly-color-primary: #1f7a43;
|
|
580
|
+
--orderly-color-primary-soft: #e7f4eb;
|
|
581
|
+
--orderly-color-primary-contrast: #ffffff;
|
|
582
|
+
--orderly-link-accent-color: var(--orderly-color-primary);
|
|
583
|
+
--orderly-selection-border: var(--orderly-color-primary);
|
|
584
|
+
--orderly-selection-background: var(--orderly-color-primary-soft);
|
|
585
|
+
--orderly-badge-background: var(--orderly-color-primary);
|
|
586
|
+
--orderly-badge-color: var(--orderly-color-primary-contrast);
|
|
587
|
+
--orderly-action-primary-background: var(--orderly-color-primary);
|
|
588
|
+
--orderly-action-primary-border: var(--orderly-color-primary);
|
|
589
|
+
--orderly-action-primary-color: var(--orderly-color-primary-contrast);
|
|
590
|
+
--orderly-action-secondary-border: var(--orderly-color-primary);
|
|
591
|
+
--orderly-action-secondary-color: var(--orderly-color-primary);
|
|
592
|
+
}
|
|
593
|
+
```
|
|
491
594
|
|
|
492
595
|
Rendering can be replaced with templates:
|
|
493
596
|
|
|
@@ -537,7 +640,7 @@ Rendering can be replaced with templates:
|
|
|
537
640
|
|
|
538
641
|
For dynamic paging, `orderly-product-grid` keeps the returned `PageResult.Continuation` as an opaque `ContinuationToken` and sends it as `SearchQuery.Continuation` when the sentinel enters the viewport. Dynamic and infinite paging do not render a load-more button. Use `paging="button"` only when a visible load-more button is desired, `paging="manual"` when another control calls `loadNextPage()`, and `paging="dynamic"` or `paging="infinite"` for automatic loading.
|
|
539
642
|
|
|
540
|
-
Every component has a default light-DOM implementation and matching template hooks. Use `data-orderly-bind` for values, `data-orderly-action` for behavior, and `data-orderly-slot` for repeated or projected content. Product tile templates can bind `basket-action-icon`, `basket-action-label`, and `basket-action-state` and use `data-orderly-action="add-to-basket"` for the add/remove toggle. Product page galleries use `data-orderly-slot="thumbnails"` and `data-orderly-action="select-image"` for custom thumbnail markup. Product rail templates can replace the outer `layout`, inner `grid`, product item, loading state, and empty state. The main template names are `layout`, `grid`, `footer`, `product`, `thumbnail`, `image`, `basket`, `basket-icon`, `line`, `checkout`, `navigation`, `item`, `search-box`, `sort`, `sort-select`, `filter-panel`, `load-more`, `address-line`, `contact-item`, `opening-hour`, and `information-link`.
|
|
643
|
+
Every component has a default light-DOM implementation and matching template hooks. Use `data-orderly-bind` for values, `data-orderly-action` for behavior, and `data-orderly-slot` for repeated or projected content. Product tile templates can bind `basket-action-icon`, `basket-action-label`, and `basket-action-state` and use `data-orderly-action="add-to-basket"` for the add/remove toggle. Product page galleries use `data-orderly-slot="thumbnails"` and `data-orderly-action="select-image"` for custom thumbnail markup. Basket templates can add `data-orderly-slot="errors"` to place order-level `DraftOrder.Errors` exactly where a shop wants them. Product rail templates can replace the outer `layout`, inner `grid`, product item, loading state, and empty state. The main template names are `layout`, `grid`, `footer`, `product`, `thumbnail`, `image`, `basket`, `basket-icon`, `line`, `checkout`, `navigation`, `item`, `search-box`, `sort`, `sort-select`, `filter-panel`, `load-more`, `address-line`, `contact-item`, `opening-hour`, and `information-link`.
|
|
541
644
|
|
|
542
645
|
Product sorting belongs to `orderly-product-grid`: assign `grid.sortOptions = [{ label, orderBy }]` or pass sort options through `orderly-collection-page.sortOptions`. The default grid layout renders the sort select above the products. Custom grid layouts should include `data-orderly-slot="sort"` where the control should appear, and custom sort templates can use `data-orderly-template="sort"` with a `<select data-orderly-action="sort">`.
|
|
543
646
|
|
|
@@ -671,15 +774,14 @@ productImage.image = searchObject.Images[0];
|
|
|
671
774
|
|
|
672
775
|
## State And Security
|
|
673
776
|
|
|
674
|
-
Basket drafts
|
|
777
|
+
Basket drafts are persisted to localStorage by default. Checkout contact and address data are stored directly on `DraftOrder.Transport`, so basket items, delivery selection, and checkout form state all share the same persisted draft. That draft can contain PII such as name, address, email, and phone. Provide a custom basket store to change or disable that behavior. This package never stores tokens, API keys, OAuth responses, or cookie values.
|
|
675
778
|
|
|
676
|
-
|
|
779
|
+
Persistence uses `LocalStorageBasketStore` and stores the full `DraftOrder`:
|
|
677
780
|
|
|
678
781
|
```ts
|
|
679
782
|
import { createBasketController } from "@orderlyshop/web-components";
|
|
680
783
|
|
|
681
784
|
basket.basketController = createBasketController({ store: myBasketStore });
|
|
682
|
-
checkout.profileStore = myCheckoutProfileStore;
|
|
683
785
|
```
|
|
684
786
|
|
|
685
787
|
Authentication remains owned by `@orderlyshop/core-client`. Browser cookie-backed sessions work through the core browser client and credentialed gRPC-web requests.
|
|
@@ -32,14 +32,6 @@ if (!skipPackageJson) {
|
|
|
32
32
|
planned.push("package.json");
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
const existing = files
|
|
36
|
-
.filter((file) => !file.skipIfExists)
|
|
37
|
-
.map((file) => file.path)
|
|
38
|
-
.filter((file) => existsSync(resolve(targetRoot, file)));
|
|
39
|
-
if (existing.length && !force) {
|
|
40
|
-
throw new Error(`Refusing to overwrite existing files: ${existing.join(", ")}. Re-run with --force to overwrite scaffold files.`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
35
|
if (dryRun) {
|
|
44
36
|
console.log(`Would initialize ${shopName} in ${relative(cwd, targetRoot) || "."}.`);
|
|
45
37
|
for (const file of planned) {
|
|
@@ -51,13 +43,15 @@ if (dryRun) {
|
|
|
51
43
|
mkdirSync(targetRoot, { recursive: true });
|
|
52
44
|
for (const file of files) {
|
|
53
45
|
const target = resolve(targetRoot, file.path);
|
|
54
|
-
|
|
55
|
-
|
|
46
|
+
const relativeTarget = relative(cwd, target);
|
|
47
|
+
const targetExists = existsSync(target);
|
|
48
|
+
if (targetExists && !force) {
|
|
49
|
+
console.warn(`Warning: ${relativeTarget} already exists; leaving it unchanged. Re-run with --force to overwrite.`);
|
|
56
50
|
continue;
|
|
57
51
|
}
|
|
58
52
|
mkdirSync(dirname(target), { recursive: true });
|
|
59
53
|
writeFileSync(target, file.content, "utf8");
|
|
60
|
-
console.log(
|
|
54
|
+
console.log(`${targetExists ? "Overwrote" : "Created"} ${relativeTarget}.`);
|
|
61
55
|
}
|
|
62
56
|
|
|
63
57
|
if (!skipPackageJson) {
|
|
@@ -84,6 +78,8 @@ function scaffoldFiles(options) {
|
|
|
84
78
|
{ path: "src/category.html", content: pageSource({ title: `Kategori | ${options.shopName}`, element: "<orderly-category-page></orderly-category-page>" }) },
|
|
85
79
|
{ path: "src/product.html", content: pageSource({ title: `Produkt | ${options.shopName}`, element: productDetailElementSource() }) },
|
|
86
80
|
{ path: "src/checkout.html", content: pageSource({ title: `Betaling | ${options.shopName}`, element: checkoutElementSource() }) },
|
|
81
|
+
{ path: "src/payment-success.html", content: pageSource({ title: `Tak for din ordre | ${options.shopName}`, element: paymentSuccessElementSource() }) },
|
|
82
|
+
{ path: "src/payment-failure.html", content: pageSource({ title: `Betaling kunne ikke gennemføres | ${options.shopName}`, element: paymentFailureElementSource() }) },
|
|
87
83
|
{ path: "src/includes/head.html", content: headIncludeSource(options) },
|
|
88
84
|
{ path: "src/includes/body-start.html", content: bodyStartSource() },
|
|
89
85
|
{ path: "src/includes/body-end.html", content: "<!-- Add shared body-end snippets here. -->\n" },
|
|
@@ -141,6 +137,7 @@ function agentsSource() {
|
|
|
141
137
|
|
|
142
138
|
- This shop was scaffolded with \`npx orderly-init-shop\` from \`@orderlyshop/web-components\`.
|
|
143
139
|
- Keep storefront logic in Orderly web components. Customize navigation, content, templates, and CSS instead of replacing package components.
|
|
140
|
+
- Start visual theming from the package \`DESIGN.md\` semantic tokens before using component-local selectors.
|
|
144
141
|
- Configure shop-wide backend, default query, navigation, page layout, and component registration in \`src/includes/head.html\` through \`configureShop(...)\`.
|
|
145
142
|
- Use \`src/navigation.ts\` for category/navigation structure. Every category needs a stable globally unique \`slug\`.
|
|
146
143
|
- Use \`src/shop-query.ts\` for the shop-wide Core \`SearchQuery\`, such as tenant/account scoping.
|
|
@@ -164,6 +161,8 @@ npm run build
|
|
|
164
161
|
npm run preview
|
|
165
162
|
\`\`\`
|
|
166
163
|
|
|
164
|
+
Test the shop locally from \`http://localhost:61677\` during normal development, or \`https://localhost:61677\` if a local TLS proxy terminates HTTPS in front of the dev server. Keep the origin on \`localhost:61677\`, because the backend CORS development policy expects that local origin.
|
|
165
|
+
|
|
167
166
|
Override the backend during local development:
|
|
168
167
|
|
|
169
168
|
\`\`\`sh
|
|
@@ -180,6 +179,7 @@ Use \`npm run generate:categories\` when you want to inspect generated source pa
|
|
|
180
179
|
|
|
181
180
|
## Customize
|
|
182
181
|
|
|
182
|
+
- For consistent theming across all components, start with the semantic token contract in \`node_modules/@orderlyshop/web-components/DESIGN.md\`.
|
|
183
183
|
- \`src/navigation.ts\` defines storefront navigation and category queries.
|
|
184
184
|
- \`src/shop-query.ts\` defines the shop-wide Core \`SearchQuery\`.
|
|
185
185
|
- \`src/includes/head.html\` calls \`configureShop(...)\`.
|
|
@@ -444,6 +444,26 @@ function checkoutElementSource() {
|
|
|
444
444
|
</orderly-checkout-page>`;
|
|
445
445
|
}
|
|
446
446
|
|
|
447
|
+
function paymentSuccessElementSource() {
|
|
448
|
+
return `<orderly-payment-success-page>
|
|
449
|
+
<div slot="utility" class="shop-utility-strip" aria-label="Kundefordele">
|
|
450
|
+
<span>Hurtig levering</span>
|
|
451
|
+
<span>Nem retur</span>
|
|
452
|
+
<span>Sikker betaling</span>
|
|
453
|
+
</div>
|
|
454
|
+
</orderly-payment-success-page>`;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function paymentFailureElementSource() {
|
|
458
|
+
return `<orderly-payment-failure-page>
|
|
459
|
+
<div slot="utility" class="shop-utility-strip" aria-label="Kundefordele">
|
|
460
|
+
<span>Hurtig levering</span>
|
|
461
|
+
<span>Nem retur</span>
|
|
462
|
+
<span>Sikker betaling</span>
|
|
463
|
+
</div>
|
|
464
|
+
</orderly-payment-failure-page>`;
|
|
465
|
+
}
|
|
466
|
+
|
|
447
467
|
function headIncludeSource(options) {
|
|
448
468
|
return `<!-- Add shared head snippets here, for example analytics, consent, or preconnect tags. -->
|
|
449
469
|
<link rel="icon" href="https://orderly.shop/home/App_Icon.svg" type="image/svg+xml">
|
|
@@ -575,7 +595,9 @@ body.orderly-navigation-menu-open {
|
|
|
575
595
|
orderly-home-page:not(:defined),
|
|
576
596
|
orderly-category-page:not(:defined),
|
|
577
597
|
orderly-product-detail-page:not(:defined),
|
|
578
|
-
orderly-checkout-page:not(:defined)
|
|
598
|
+
orderly-checkout-page:not(:defined),
|
|
599
|
+
orderly-payment-success-page:not(:defined),
|
|
600
|
+
orderly-payment-failure-page:not(:defined) {
|
|
579
601
|
display: block;
|
|
580
602
|
min-height: 100vh;
|
|
581
603
|
background: #f6f8f9;
|
|
@@ -584,7 +606,9 @@ orderly-checkout-page:not(:defined) {
|
|
|
584
606
|
orderly-home-page:not(:defined)::before,
|
|
585
607
|
orderly-category-page:not(:defined)::before,
|
|
586
608
|
orderly-product-detail-page:not(:defined)::before,
|
|
587
|
-
orderly-checkout-page:not(:defined)::before
|
|
609
|
+
orderly-checkout-page:not(:defined)::before,
|
|
610
|
+
orderly-payment-success-page:not(:defined)::before,
|
|
611
|
+
orderly-payment-failure-page:not(:defined)::before {
|
|
588
612
|
content: "";
|
|
589
613
|
display: block;
|
|
590
614
|
height: 62px;
|
|
@@ -843,9 +867,10 @@ Options:
|
|
|
843
867
|
|
|
844
868
|
Agent workflow:
|
|
845
869
|
1. If no shop scaffold exists, run: npx orderly-init-shop
|
|
846
|
-
2.
|
|
847
|
-
3.
|
|
848
|
-
4. Customize src/
|
|
870
|
+
2. Existing scaffold files are skipped with warnings. Use --force only when the user explicitly wants overwrite.
|
|
871
|
+
3. Ask the user for the shop account id. Re-run with --account-id <id> or edit src/shop-query.ts.
|
|
872
|
+
4. Customize src/navigation.ts with categories and SearchQuery values.
|
|
873
|
+
5. Customize src/templates/*.html and src/style.css for the shop look and feel.
|
|
849
874
|
`);
|
|
850
875
|
}
|
|
851
876
|
|
package/custom-elements.json
CHANGED
|
@@ -130,6 +130,39 @@
|
|
|
130
130
|
{ "name": "orderly-error", "type": { "text": "CustomEvent<{ error: unknown }>" } }
|
|
131
131
|
]
|
|
132
132
|
},
|
|
133
|
+
{
|
|
134
|
+
"kind": "class",
|
|
135
|
+
"name": "OrderlyPaymentSuccessPageElement",
|
|
136
|
+
"tagName": "orderly-payment-success-page",
|
|
137
|
+
"customElement": true,
|
|
138
|
+
"description": "Payment success callback page that loads a stored order from a base64 encoded protobuf OrderId.",
|
|
139
|
+
"attributes": [
|
|
140
|
+
{ "name": "order-id", "description": "Base64 encoded protobuf OrderId. Defaults to the orderid URL parameter." },
|
|
141
|
+
{ "name": "brand-label", "description": "Brand text." },
|
|
142
|
+
{ "name": "title", "description": "Page heading." },
|
|
143
|
+
{ "name": "description", "description": "Page intro text." },
|
|
144
|
+
{ "name": "reference-label", "description": "Order reference label." },
|
|
145
|
+
{ "name": "order-title", "description": "Order summary heading." },
|
|
146
|
+
{ "name": "empty-label", "description": "Text shown when the order is not found locally." }
|
|
147
|
+
]
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"kind": "class",
|
|
151
|
+
"name": "OrderlyPaymentFailurePageElement",
|
|
152
|
+
"tagName": "orderly-payment-failure-page",
|
|
153
|
+
"customElement": true,
|
|
154
|
+
"description": "Payment failure callback page that lets the customer retry the stored order payment link.",
|
|
155
|
+
"attributes": [
|
|
156
|
+
{ "name": "order-id", "description": "Base64 encoded protobuf OrderId. Defaults to the orderid URL parameter." },
|
|
157
|
+
{ "name": "brand-label", "description": "Brand text." },
|
|
158
|
+
{ "name": "title", "description": "Page heading." },
|
|
159
|
+
{ "name": "description", "description": "Page intro text." },
|
|
160
|
+
{ "name": "reference-label", "description": "Order reference label." },
|
|
161
|
+
{ "name": "order-title", "description": "Order summary heading." },
|
|
162
|
+
{ "name": "empty-label", "description": "Text shown when the order is not found locally." },
|
|
163
|
+
{ "name": "retry-label", "description": "Retry payment link label." }
|
|
164
|
+
]
|
|
165
|
+
},
|
|
133
166
|
{
|
|
134
167
|
"kind": "class",
|
|
135
168
|
"name": "OrderlyProductGridElement",
|
|
@@ -278,7 +311,7 @@
|
|
|
278
311
|
"name": "OrderlyProductTileElement",
|
|
279
312
|
"tagName": "orderly-product-tile",
|
|
280
313
|
"customElement": true,
|
|
281
|
-
"description": "Product tile for SearchObject with product selection
|
|
314
|
+
"description": "Product tile for SearchObject with product selection, basket toggle behavior, and configurable share-url binding for raw share URLs or physical product routes.",
|
|
282
315
|
"attributes": [
|
|
283
316
|
{ "name": "add-label", "description": "Add-to-basket accessible label." },
|
|
284
317
|
{ "name": "remove-label", "description": "Remove-from-basket accessible label." }
|
|
@@ -369,7 +402,7 @@
|
|
|
369
402
|
"name": "OrderlyBasketElement",
|
|
370
403
|
"tagName": "orderly-basket",
|
|
371
404
|
"customElement": true,
|
|
372
|
-
"description": "DraftOrder basket overview with persistence, remove actions, quantity display,
|
|
405
|
+
"description": "DraftOrder basket overview with persistence, remove actions, quantity display, backend VerifyDraft summary rendering, order-level and line-level errors, and checkout link.",
|
|
373
406
|
"attributes": [
|
|
374
407
|
{ "name": "base-url", "description": "Backend URL used when no client property is assigned." },
|
|
375
408
|
{ "name": "empty-label", "description": "Empty basket text." },
|
|
@@ -423,9 +456,9 @@
|
|
|
423
456
|
"name": "OrderlyNavigationMenuElement",
|
|
424
457
|
"tagName": "orderly-navigation",
|
|
425
458
|
"customElement": true,
|
|
426
|
-
"description": "Navigation menu with vertical or
|
|
459
|
+
"description": "Navigation menu with vertical, horizontal, or burgermenu layouts, nested disclosure behavior, desktop tiered horizontal navigation support, horizontal overflow scrolling, projected inline content, and selection events.",
|
|
427
460
|
"attributes": [
|
|
428
|
-
{ "name": "layout", "description": "Navigation layout. Use vertical for a side menu
|
|
461
|
+
{ "name": "layout", "description": "Navigation layout. Use vertical for a side menu, horizontal for a tiered desktop menu with a scrolling top row, full-width second row, and third-level dropdowns, or burgermenu for an icon trigger that opens a nested disclosure menu inside a floating panel." },
|
|
429
462
|
{ "name": "sticky", "description": "Set to false to opt out of sticky positioning." }
|
|
430
463
|
],
|
|
431
464
|
"members": [
|