@orderlyshop/web-components 0.1.0-build.7045

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.
Files changed (71) hide show
  1. package/AGENTS.md +110 -0
  2. package/README.md +685 -0
  3. package/bin/orderly-build-category-pages.mjs +160 -0
  4. package/bin/orderly-generate-category-pages.mjs +308 -0
  5. package/bin/orderly-hydrate-static-pages.mjs +595 -0
  6. package/bin/orderly-init-navigation.mjs +327 -0
  7. package/bin/orderly-init-shop.mjs +876 -0
  8. package/bin/orderly-init-taxonomy.mjs +2 -0
  9. package/bin/orderly-publish-site.mjs +342 -0
  10. package/custom-elements.json +505 -0
  11. package/dist/browser/orderly-web-components.define.global.js +3551 -0
  12. package/dist/browser/orderly-web-components.define.global.js.map +1 -0
  13. package/dist/browser/orderly-web-components.global.js +3551 -0
  14. package/dist/browser/orderly-web-components.global.js.map +1 -0
  15. package/dist/default-shop-DgX6uy10.d.ts +221 -0
  16. package/dist/default-shop.d.ts +6 -0
  17. package/dist/default-shop.js +762 -0
  18. package/dist/default-shop.js.map +1 -0
  19. package/dist/define-BNMhl19n.d.ts +9 -0
  20. package/dist/define.d.ts +2 -0
  21. package/dist/define.js +11094 -0
  22. package/dist/define.js.map +1 -0
  23. package/dist/index.d.ts +683 -0
  24. package/dist/index.js +11417 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/navigation.d.ts +61 -0
  27. package/dist/navigation.js +1125 -0
  28. package/dist/navigation.js.map +1 -0
  29. package/dist/query.d.ts +31 -0
  30. package/dist/query.js +115 -0
  31. package/dist/query.js.map +1 -0
  32. package/dist/registry-CPDecU3g.d.ts +6 -0
  33. package/dist/shop-BgQhGRzS.d.ts +173 -0
  34. package/dist/shop-query.d.ts +8 -0
  35. package/dist/shop-query.js +100 -0
  36. package/dist/shop-query.js.map +1 -0
  37. package/dist/shop.d.ts +8 -0
  38. package/dist/shop.js +11187 -0
  39. package/dist/shop.js.map +1 -0
  40. package/dist/stores.d.ts +46 -0
  41. package/dist/stores.js +145 -0
  42. package/dist/stores.js.map +1 -0
  43. package/dist/taxonomy.d.ts +35 -0
  44. package/dist/taxonomy.js +247 -0
  45. package/dist/taxonomy.js.map +1 -0
  46. package/dist/types-Bjez59Hr.d.ts +96 -0
  47. package/docs/components/README.md +708 -0
  48. package/docs/components/product-grid.md +182 -0
  49. package/docs/components/product-rail.md +174 -0
  50. package/examples/shop/README.md +72 -0
  51. package/examples/shop/package.json +28 -0
  52. package/examples/shop/src/category.html +20 -0
  53. package/examples/shop/src/checkout.html +21 -0
  54. package/examples/shop/src/forretningsbetingelser.html +81 -0
  55. package/examples/shop/src/includes/body-end.html +1 -0
  56. package/examples/shop/src/includes/body-start.html +3 -0
  57. package/examples/shop/src/includes/head.html +32 -0
  58. package/examples/shop/src/index.html +25 -0
  59. package/examples/shop/src/navigation.ts +154 -0
  60. package/examples/shop/src/product.html +24 -0
  61. package/examples/shop/src/templates/page-layouts.html +162 -0
  62. package/examples/shop/src/templates/shop-footer.html +76 -0
  63. package/examples/shop/tsconfig.json +32 -0
  64. package/examples/shop/vite.config.mjs +190 -0
  65. package/html-custom-data.json +279 -0
  66. package/package.json +118 -0
  67. package/server/README.md +111 -0
  68. package/server/apache/.htaccess +18 -0
  69. package/server/nginx/orderly-products.conf +24 -0
  70. package/server/node/product-snapshot-server.mjs +133 -0
  71. package/server/php/orderly-product.php +204 -0
@@ -0,0 +1,154 @@
1
+ import type { NavigationDefinition } from "@orderlyshop/web-components";
2
+
3
+ export const navigationDefinitions: NavigationDefinition[] = [
4
+ {
5
+ label: "Tøj & Mode",
6
+ slug: "toej-og-mode",
7
+ heroImage: "https://images.unsplash.com/photo-1496747611176-843222e1e57c?auto=format&fit=crop&w=1800&q=80",
8
+ query: { tags: ["børnetøj"] },
9
+ children: [
10
+ { label: "Dame", slug: "dame", query: { query: "dame tøj kjoler jakker mode" } },
11
+ { label: "Herre", slug: "herre", query: { query: "herre tøj skjorter jakker mode" } },
12
+ { label: "Børn & Baby", slug: "boern-og-baby", query: { query: "børn baby tøj" } },
13
+ { label: "Sport & Activewear", slug: "sport-og-activewear", query: { query: "sport activewear træning tøj" } },
14
+ { label: "Accessories", slug: "accessories", query: { query: "accessories tasker bælter mode" } }
15
+ ]
16
+ },
17
+ {
18
+ label: "Sko",
19
+ slug: "sko",
20
+ heroImage: "https://images.unsplash.com/photo-1542291026-7eec264c27ff?auto=format&fit=crop&w=1800&q=80",
21
+ query: { query: "Nike sko sneakers" },
22
+ children: [
23
+ { label: "Dame sko", slug: "dame-sko", query: { query: "dame sko sneakers støvler" } },
24
+ { label: "Herre sko", slug: "herre-sko", query: { query: "herre sko sneakers støvler" } },
25
+ { label: "Børnesko", slug: "boernesko", query: { query: "børn sko sneakers sandaler" } },
26
+ { label: "Sportssko", slug: "sportssko", query: { query: "sportssko løbesko træning" } },
27
+ { label: "Special", slug: "special", query: { query: "special sko sikkerhedssko vandresko" } }
28
+ ]
29
+ },
30
+ {
31
+ label: "Bolig & Indretning",
32
+ slug: "bolig-og-indretning",
33
+ heroImage: "https://images.unsplash.com/photo-1618221195710-dd6b41faaea6?auto=format&fit=crop&w=1800&q=80",
34
+ query: { tags: ["bolig"] },
35
+ children: [
36
+ { label: "Møbler", slug: "moebler", query: { query: "møbler sofa stol bord" } },
37
+ { label: "Belysning", slug: "belysning", query: { query: "lampe belysning bolig" } },
38
+ { label: "Tekstiler", slug: "tekstiler", query: { query: "tekstiler puder tæpper gardiner" } },
39
+ { label: "Køkkenudstyr", slug: "koekkenudstyr", query: { query: "køkkenudstyr service glas gryder" } },
40
+ { label: "Dekoration & Kunst", slug: "dekoration-og-kunst", query: { query: "kunst dekoration bolig design" } }
41
+ ]
42
+ },
43
+ {
44
+ label: "Elektronik",
45
+ slug: "elektronik",
46
+ heroImage: "https://images.unsplash.com/photo-1518770660439-4636190af475?auto=format&fit=crop&w=1800&q=80",
47
+ query: { tags: ["elektronik"] },
48
+ children: [
49
+ { label: "Computere & Tablets", slug: "computere-og-tablets", query: { query: "computer tablet laptop" } },
50
+ { label: "Mobil & Tilbehør", slug: "mobil-og-tilbehoer", query: { query: "mobil telefon cover oplader" } },
51
+ { label: "TV & Lyd", slug: "tv-og-lyd", query: { query: "tv lyd højtaler headset" } },
52
+ { label: "Gaming", slug: "gaming", query: { query: "gaming konsol controller spil" } },
53
+ { label: "Smart Home", slug: "smart-home", query: { query: "smart home lys sensor kamera" } }
54
+ ]
55
+ },
56
+ {
57
+ label: "Børn & Legetøj",
58
+ slug: "boern-og-legetoej",
59
+ heroImage: "https://images.unsplash.com/photo-1515488042361-ee00e0ddd4e4?auto=format&fit=crop&w=1800&q=80",
60
+ query: { tags: ["legetøj "] },
61
+ children: [
62
+ { label: "Legetøj", slug: "legetoej", query: { query: "legetøj bamser figurer spil" } },
63
+ { label: "Babyudstyr", slug: "babyudstyr", query: { query: "babyudstyr barnevogn autostol" } },
64
+ { label: "Børnemøbler", slug: "boernemoebler", query: { query: "børnemøbler seng stol bord" } },
65
+ { label: "Udendørs leg", slug: "udendoers-leg", query: { query: "udendørs leg trampolin sandkasse" } },
66
+ { label: "Læring & Kreativitet", slug: "laering-og-kreativitet", query: { query: "læring kreativitet hobby børn" } }
67
+ ]
68
+ },
69
+ {
70
+ label: "Sport & Fritid",
71
+ slug: "sport-og-fritid",
72
+ heroImage: "https://images.unsplash.com/photo-1517649763962-0c623066013b?auto=format&fit=crop&w=1800&q=80",
73
+ query: { tags: ["sport-fritid"] },
74
+ children: [
75
+ { label: "Fitness & Træning", slug: "fitness-og-traening", query: { query: "fitness træning sport" } },
76
+ { label: "Outdoor & Camping", slug: "outdoor-og-camping", query: { query: "outdoor camping vandring telt" } },
77
+ { label: "Cykler & Tilbehør", slug: "cykler-og-tilbehoer", query: { query: "cykler cykelhjelm cykeltilbehør" } },
78
+ { label: "Vandsport", slug: "vandsport", query: { query: "vandsport svømning paddleboard" } },
79
+ { label: "Hobby & DIY", slug: "hobby-og-diy", query: { query: "hobby diy værktøj kreativ" } }
80
+ ]
81
+ },
82
+ {
83
+ label: "Skønhed & Personlig Pleje",
84
+ slug: "skoenhed-og-personlig-pleje",
85
+ heroImage: "https://images.unsplash.com/photo-1522335789203-aabd1fc54bc9?auto=format&fit=crop&w=1800&q=80",
86
+ query: { query: "skønhed makeup hudpleje" },
87
+ children: [
88
+ { label: "Hudpleje", slug: "hudpleje", query: { query: "hudpleje creme serum rens" } },
89
+ { label: "Hårpleje", slug: "haarpleje", query: { query: "hårpleje shampoo balsam styling" } },
90
+ { label: "Makeup", slug: "makeup", query: { query: "makeup kosmetik skønhed" } },
91
+ { label: "Parfume", slug: "parfume", query: { query: "parfume duft fragrance" } },
92
+ { label: "Personlig pleje", slug: "personlig-pleje", query: { query: "personlig pleje barbering hygiene" } }
93
+ ]
94
+ },
95
+ {
96
+ label: "Smykker & Ure",
97
+ slug: "smykker-og-ure",
98
+ heroImage: "https://images.unsplash.com/photo-1515562141207-7a88fb7ce338?auto=format&fit=crop&w=1800&q=80",
99
+ query: { query: "smykker ure accessories" },
100
+ children: [
101
+ { label: "Smykker", slug: "smykker", query: { query: "smykker ring halskæde armbånd" } },
102
+ { label: "Ure", slug: "ure", query: { query: "ure smartwatch klassisk ur" } },
103
+ { label: "Luksusvarer", slug: "luksusvarer", query: { query: "luksusvarer designer premium" } }
104
+ ]
105
+ },
106
+ {
107
+ label: "Bøger, Medier & Samlerobjekter",
108
+ slug: "boeger-medier-og-samlerobjekter",
109
+ heroImage: "https://images.unsplash.com/photo-1524995997946-a1c2e315a42f?auto=format&fit=crop&w=1800&q=80",
110
+ query: { query: "bøger medier samlerobjekter" },
111
+ children: [
112
+ { label: "Bøger", slug: "boeger", query: { query: "bøger roman fagbog børnebog" } },
113
+ { label: "Film & Musik", slug: "film-og-musik", query: { query: "film musik dvd vinyl" } },
114
+ { label: "Spil", slug: "spil", query: { query: "spil brætspil konsolspil" } },
115
+ { label: "Samlerobjekter", slug: "samlerobjekter", query: { query: "samlerobjekter figurer vintage" } }
116
+ ]
117
+ },
118
+ {
119
+ label: "Bil & Transport",
120
+ slug: "bil-og-transport",
121
+ heroImage: "https://images.unsplash.com/photo-1503376780353-7e6692767b70?auto=format&fit=crop&w=1800&q=80",
122
+ query: { query: "bil transport tilbehør" },
123
+ children: [
124
+ { label: "Reservedele", slug: "reservedele", query: { query: "reservedele bil dele" } },
125
+ { label: "Tilbehør", slug: "tilbehoer", query: { query: "biltilbehør måtter holder" } },
126
+ { label: "Cykler & el-løbehjul", slug: "cykler-og-el-loebehjul", query: { query: "cykler el-løbehjul transport" } },
127
+ { label: "Autopleje", slug: "autopleje", query: { query: "autopleje voks rengøring bil" } }
128
+ ]
129
+ },
130
+ {
131
+ label: "Kontor & Erhverv",
132
+ slug: "kontor-og-erhverv",
133
+ heroImage: "https://images.unsplash.com/photo-1497366754035-f200968a6e72?auto=format&fit=crop&w=1800&q=80",
134
+ query: { query: "kontor erhverv udstyr" },
135
+ children: [
136
+ { label: "Kontorudstyr", slug: "kontorudstyr", query: { query: "kontorudstyr papir printer stol" } },
137
+ { label: "Lager & logistik", slug: "lager-og-logistik", query: { query: "lager logistik reol emballage" } },
138
+ { label: "Butiksinventar", slug: "butiksinventar", query: { query: "butiksinventar display kasseapparat" } },
139
+ { label: "Industrielt udstyr", slug: "industrielt-udstyr", query: { query: "industrielt udstyr maskiner værktøj" } }
140
+ ]
141
+ },
142
+ {
143
+ label: "Diverse & Specialvarer",
144
+ slug: "diverse-og-specialvarer",
145
+ heroImage: "https://images.unsplash.com/photo-1516975080664-ed2fc6a32937?auto=format&fit=crop&w=1800&q=80",
146
+ query: { query: "vintage antik specialvarer" },
147
+ children: [
148
+ { label: "Vintage & Antik", slug: "vintage-og-antik", query: { query: "vintage antik retro" } },
149
+ { label: "Restpartier", slug: "restpartier", query: { query: "restpartier outlet tilbud" } },
150
+ { label: "Upcycle materialer", slug: "upcycle-materialer", query: { query: "upcycle materialer genbrug" } },
151
+ { label: "Ukategoriseret", slug: "ukategoriseret", query: { query: "ukategoriseret diverse" } }
152
+ ]
153
+ }
154
+ ];
@@ -0,0 +1,24 @@
1
+ <!doctype html>
2
+ <html lang="da">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Produkt | Orderly Example Shop</title>
7
+ <!-- orderly-head-includes -->
8
+ </head>
9
+ <body>
10
+ <!-- orderly-body-start-includes -->
11
+ <orderly-product-detail-page
12
+ add-label="Læg i kurven"
13
+ loading-label="Indlæser produkt..."
14
+ not-found-label="Produktet blev ikke fundet."
15
+ error-label="Produktet kunne ikke indlæses.">
16
+ <div slot="utility" class="shop-utility-strip" aria-label="Kundefordele">
17
+ <span>Gratis levering over 499 kr.</span>
18
+ <span>30 dages returret</span>
19
+ <span>Sikker betaling</span>
20
+ </div>
21
+ </orderly-product-detail-page>
22
+ <!-- orderly-body-end-includes -->
23
+ </body>
24
+ </html>
@@ -0,0 +1,162 @@
1
+ <template data-orderly-template="layout" data-orderly-for="home-page">
2
+ <orderly-page-layout>
3
+ <div slot="utility" data-orderly-slot="utility"></div>
4
+ <a slot="header" class="orderly-home-page__brand" id="brandLink" href="/">
5
+ <span data-orderly-bind="brandLabel"></span>
6
+ </a>
7
+ <orderly-search-box slot="header-actions" id="homeSearch" mode="icon"></orderly-search-box>
8
+ <orderly-basket-icon slot="header-actions" id="basketIcon"></orderly-basket-icon>
9
+ <div slot="header-actions" data-orderly-slot="header-actions"></div>
10
+ <orderly-navigation slot="primary-nav" id="homeNavigation" layout="horizontal" aria-label="Hovednavigation"></orderly-navigation>
11
+ <div slot="content-before" data-orderly-slot="content-before"></div>
12
+ <section slot="content" class="orderly-home-page">
13
+ <header class="orderly-home-page__intro">
14
+ <p class="orderly-home-page__eyebrow" data-orderly-bind="eyebrow"></p>
15
+ <h1 data-orderly-bind="title"></h1>
16
+ </header>
17
+ <div id="homeRails" class="orderly-home-page__rails" data-orderly-slot="rails"></div>
18
+ </section>
19
+ <div slot="content-after" data-orderly-slot="content-after"></div>
20
+ <orderly-shop-footer slot="footer"></orderly-shop-footer>
21
+ <div slot="footer" data-orderly-slot="footer"></div>
22
+ <aside slot="overlay" class="orderly-category-page__basket-panel" id="basketPanel" hidden>
23
+ <div class="orderly-category-page__basket-panel-header">
24
+ <h2>Kurv</h2>
25
+ <button id="basketClose" type="button">Luk</button>
26
+ </div>
27
+ <orderly-basket id="basket"></orderly-basket>
28
+ </aside>
29
+ <dialog slot="overlay" class="orderly-category-page__product-dialog" id="productDialog">
30
+ <div class="orderly-category-page__product-dialog-actions">
31
+ <a class="orderly-category-page__product-dialog-expand" id="productExpand" href="/product.html" aria-label="Åbn produktside" title="Åbn produktside" hidden>
32
+ <svg viewBox="0 0 24 24" focusable="false" aria-hidden="true" width="20" height="20">
33
+ <path d="M8 7H6.75A1.75 1.75 0 0 0 5 8.75v8.5c0 .97.78 1.75 1.75 1.75h8.5c.97 0 1.75-.78 1.75-1.75V16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"></path>
34
+ <path d="M13 5h6v6M12 12l7-7" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"></path>
35
+ </svg>
36
+ </a>
37
+ <button class="orderly-category-page__product-dialog-close" id="productClose" type="button" aria-label="Luk" title="Luk">
38
+ <svg viewBox="0 0 24 24" focusable="false" aria-hidden="true" width="20" height="20">
39
+ <path d="m6.4 5 5.6 5.6L17.6 5 19 6.4 13.4 12l5.6 5.6-1.4 1.4-5.6-5.6L6.4 19 5 17.6l5.6-5.6L5 6.4 6.4 5Z" fill="currentColor"></path>
40
+ </svg>
41
+ </button>
42
+ </div>
43
+ <orderly-product-page id="productDetail" add-label="Læg i kurven"></orderly-product-page>
44
+ </dialog>
45
+ <div slot="overlay" data-orderly-slot="overlay"></div>
46
+ </orderly-page-layout>
47
+ </template>
48
+
49
+ <template data-orderly-template="layout" data-orderly-for="category-page">
50
+ <orderly-page-layout>
51
+ <div slot="utility" data-orderly-slot="utility"></div>
52
+ <a slot="header" class="orderly-category-page__brand" id="brandLink" href="/">
53
+ <span data-orderly-bind="brandLabel"></span>
54
+ </a>
55
+ <orderly-search-box slot="header-actions" id="categorySearch" mode="icon" for="productGrid" data-orderly-bind="searchPlaceholder"></orderly-search-box>
56
+ <orderly-basket-icon slot="header-actions" id="basketIcon"></orderly-basket-icon>
57
+ <div slot="header-actions" data-orderly-slot="header-actions"></div>
58
+ <orderly-navigation slot="primary-nav" id="categoryNavigation" layout="horizontal" aria-label="Hovednavigation"></orderly-navigation>
59
+ <div slot="content-before" data-orderly-slot="content-before"></div>
60
+ <orderly-collection-page slot="content" id="collectionPage">
61
+ <template data-orderly-template="grid">
62
+ <orderly-product-grid id="productGrid" paging="dynamic">
63
+ <template data-orderly-template="empty">
64
+ <p class="orderly-category-page__empty-state" data-orderly-bind="emptyLabel"></p>
65
+ </template>
66
+ <template data-orderly-template="product">
67
+ <orderly-product-tile class="orderly-category-page__product-card" add-label="Læg i kurven" remove-label="Fjern fra kurven"></orderly-product-tile>
68
+ </template>
69
+ </orderly-product-grid>
70
+ </template>
71
+ </orderly-collection-page>
72
+ <div slot="content-after" data-orderly-slot="content-after"></div>
73
+ <orderly-shop-footer slot="footer"></orderly-shop-footer>
74
+ <div slot="footer" data-orderly-slot="footer"></div>
75
+ <aside slot="overlay" class="orderly-category-page__basket-panel" id="basketPanel" hidden>
76
+ <div class="orderly-category-page__basket-panel-header">
77
+ <h2>Kurv</h2>
78
+ <button id="basketClose" type="button">Luk</button>
79
+ </div>
80
+ <orderly-basket id="basket" empty-label="Kurven er tom." continue-label="Fortsæt med at handle" quantity-label="Antal" remove-label="Fjern"></orderly-basket>
81
+ </aside>
82
+ <dialog slot="overlay" class="orderly-category-page__product-dialog" id="productDialog">
83
+ <div class="orderly-category-page__product-dialog-actions">
84
+ <a class="orderly-category-page__product-dialog-expand" id="productExpand" href="/product.html" aria-label="Åbn produktside" title="Åbn produktside" hidden>
85
+ <svg viewBox="0 0 24 24" focusable="false" aria-hidden="true" width="20" height="20">
86
+ <path d="M8 7H6.75A1.75 1.75 0 0 0 5 8.75v8.5c0 .97.78 1.75 1.75 1.75h8.5c.97 0 1.75-.78 1.75-1.75V16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"></path>
87
+ <path d="M13 5h6v6M12 12l7-7" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"></path>
88
+ </svg>
89
+ </a>
90
+ <button class="orderly-category-page__product-dialog-close" id="productClose" type="button" aria-label="Luk" title="Luk">
91
+ <svg viewBox="0 0 24 24" focusable="false" aria-hidden="true" width="20" height="20">
92
+ <path d="m6.4 5 5.6 5.6L17.6 5 19 6.4 13.4 12l5.6 5.6-1.4 1.4-5.6-5.6L6.4 19 5 17.6l5.6-5.6L5 6.4 6.4 5Z" fill="currentColor"></path>
93
+ </svg>
94
+ </button>
95
+ </div>
96
+ <orderly-product-page id="productDetail" add-label="Læg i kurven"></orderly-product-page>
97
+ </dialog>
98
+ <div slot="overlay" data-orderly-slot="overlay"></div>
99
+ </orderly-page-layout>
100
+ </template>
101
+
102
+ <template data-orderly-template="layout" data-orderly-for="product-detail-page">
103
+ <orderly-page-layout>
104
+ <div slot="utility" data-orderly-slot="utility"></div>
105
+ <a slot="header" class="orderly-product-detail-page__brand" id="brandLink" href="/">
106
+ <span data-orderly-bind="brandLabel"></span>
107
+ </a>
108
+ <orderly-basket-icon slot="header-actions" id="basketIcon"></orderly-basket-icon>
109
+ <div slot="header-actions" data-orderly-slot="header-actions"></div>
110
+ <orderly-navigation slot="primary-nav" id="productNavigation" layout="horizontal" aria-label="Hovednavigation"></orderly-navigation>
111
+ <div slot="content-before" data-orderly-slot="content-before"></div>
112
+ <section slot="content" class="orderly-product-detail-page">
113
+ <p class="orderly-product-detail-page__status" data-orderly-bind="status"></p>
114
+ <section class="orderly-product-detail-page__product" id="productContainer" aria-live="polite">
115
+ <orderly-product-page id="productDetail"></orderly-product-page>
116
+ </section>
117
+ </section>
118
+ <div slot="content-after" data-orderly-slot="content-after"></div>
119
+ <orderly-shop-footer slot="footer"></orderly-shop-footer>
120
+ <div slot="footer" data-orderly-slot="footer"></div>
121
+ <aside slot="overlay" class="orderly-product-detail-page__basket-panel" id="basketPanel" hidden>
122
+ <div class="orderly-product-detail-page__basket-panel-header">
123
+ <h2>Kurv</h2>
124
+ <button id="basketClose" type="button">Luk</button>
125
+ </div>
126
+ <orderly-basket id="basket"></orderly-basket>
127
+ </aside>
128
+ <div slot="overlay" data-orderly-slot="overlay"></div>
129
+ </orderly-page-layout>
130
+ </template>
131
+
132
+ <template data-orderly-template="layout" data-orderly-for="checkout-page">
133
+ <orderly-page-layout>
134
+ <div slot="utility" data-orderly-slot="utility"></div>
135
+ <a slot="header" class="orderly-checkout-page__brand" id="brandLink" href="/">
136
+ <span data-orderly-bind="brandLabel"></span>
137
+ </a>
138
+ <div slot="header-actions" data-orderly-slot="header-actions"></div>
139
+ <orderly-navigation slot="primary-nav" id="checkoutNavigation" layout="horizontal" aria-label="Hovednavigation"></orderly-navigation>
140
+ <div slot="content-before" data-orderly-slot="content-before"></div>
141
+ <section slot="content" class="orderly-checkout-page">
142
+ <header class="orderly-checkout-page__header">
143
+ <h1 data-orderly-bind="title"></h1>
144
+ <p data-orderly-bind="description"></p>
145
+ </header>
146
+ <div class="orderly-checkout-page__grid">
147
+ <section class="orderly-checkout-page__card" aria-labelledby="checkoutFormTitle">
148
+ <h2 id="checkoutFormTitle" class="orderly-checkout-page__visually-hidden">Betaling</h2>
149
+ <orderly-checkout id="checkout"></orderly-checkout>
150
+ </section>
151
+ <aside class="orderly-checkout-page__card orderly-checkout-page__card--summary" aria-labelledby="checkoutBasketTitle">
152
+ <h2 id="checkoutBasketTitle" data-orderly-bind="orderTitle"></h2>
153
+ <orderly-basket id="basket" checkout-action="hide"></orderly-basket>
154
+ </aside>
155
+ </div>
156
+ </section>
157
+ <div slot="content-after" data-orderly-slot="content-after"></div>
158
+ <orderly-shop-footer slot="footer"></orderly-shop-footer>
159
+ <div slot="footer" data-orderly-slot="footer"></div>
160
+ <div slot="overlay" data-orderly-slot="overlay"></div>
161
+ </orderly-page-layout>
162
+ </template>
@@ -0,0 +1,76 @@
1
+ <template data-orderly-template="footer" data-orderly-for="shop-footer">
2
+ <div class="orderly-shop-footer" aria-label="Sidefod">
3
+ <div class="orderly-shop-footer__brand">
4
+ <a class="orderly-shop-footer__logo" href="/" aria-label="Orderly Example Shop">
5
+ <img class="orderly-shop-footer__logo-image" src="https://orderly.shop/home/App_Icon.svg" alt="Orderly Example Shop">
6
+ </a>
7
+ <p class="orderly-shop-footer__about">
8
+ En demo webshop bygget med Orderly web components. Indholdet er eksempeldata, så layout, navigation, kurv og checkout kan prøves uden en shopspecifik frontend-stack.
9
+ </p>
10
+ </div>
11
+
12
+ <section class="orderly-shop-footer__section" aria-labelledby="exampleFooterAddress">
13
+ <h2 id="exampleFooterAddress" class="orderly-shop-footer__heading">Adresse</h2>
14
+ <address class="orderly-shop-footer__address">
15
+ <span>Orderly Example Shop ApS</span>
16
+ <span>Demo Allé 12</span>
17
+ <span>8000 Aarhus C</span>
18
+ <span>Danmark</span>
19
+ </address>
20
+ </section>
21
+
22
+ <section class="orderly-shop-footer__section" aria-labelledby="exampleFooterContact">
23
+ <h2 id="exampleFooterContact" class="orderly-shop-footer__heading">Kontakt</h2>
24
+ <ul class="orderly-shop-footer__list">
25
+ <li class="orderly-shop-footer__contact-item">
26
+ <span class="orderly-shop-footer__contact-label">Email</span>
27
+ <a class="orderly-shop-footer__contact-value" href="mailto:hello@example.orderly.shop">hello@example.orderly.shop</a>
28
+ </li>
29
+ <li class="orderly-shop-footer__contact-item">
30
+ <span class="orderly-shop-footer__contact-label">Telefon</span>
31
+ <a class="orderly-shop-footer__contact-value" href="tel:+4512345678">+45 12 34 56 78</a>
32
+ </li>
33
+ <li class="orderly-shop-footer__contact-item">
34
+ <span class="orderly-shop-footer__contact-label">Kundeservice</span>
35
+ <a class="orderly-shop-footer__contact-value" href="https://support.example.orderly.shop">support.example.orderly.shop</a>
36
+ </li>
37
+ </ul>
38
+ </section>
39
+
40
+ <section class="orderly-shop-footer__section" aria-labelledby="exampleFooterHours">
41
+ <h2 id="exampleFooterHours" class="orderly-shop-footer__heading">Åbningstider</h2>
42
+ <dl class="orderly-shop-footer__hours">
43
+ <div class="orderly-shop-footer__hours-row">
44
+ <dt class="orderly-shop-footer__hours-label">Mandag - fredag</dt>
45
+ <dd class="orderly-shop-footer__hours-value">09.00 - 17.00</dd>
46
+ </div>
47
+ <div class="orderly-shop-footer__hours-row">
48
+ <dt class="orderly-shop-footer__hours-label">Lørdag</dt>
49
+ <dd class="orderly-shop-footer__hours-value">10.00 - 14.00</dd>
50
+ </div>
51
+ <div class="orderly-shop-footer__hours-row">
52
+ <dt class="orderly-shop-footer__hours-label">Søndag</dt>
53
+ <dd class="orderly-shop-footer__hours-value">Lukket</dd>
54
+ </div>
55
+ </dl>
56
+ </section>
57
+
58
+ <section class="orderly-shop-footer__section" aria-labelledby="exampleFooterInformation">
59
+ <h2 id="exampleFooterInformation" class="orderly-shop-footer__heading">Information</h2>
60
+ <ul class="orderly-shop-footer__list">
61
+ <li class="orderly-shop-footer__link-item">
62
+ <a class="orderly-shop-footer__link" href="/forretningsbetingelser.html">Forretningsbetingelser</a>
63
+ </li>
64
+ <li class="orderly-shop-footer__link-item">
65
+ <a class="orderly-shop-footer__link" href="/information/levering-og-retur/">Levering og retur</a>
66
+ </li>
67
+ <li class="orderly-shop-footer__link-item">
68
+ <a class="orderly-shop-footer__link" href="/information/privatlivspolitik/">Privatlivspolitik</a>
69
+ </li>
70
+ <li class="orderly-shop-footer__link-item">
71
+ <a class="orderly-shop-footer__link" href="/information/kontakt/">Kontakt os</a>
72
+ </li>
73
+ </ul>
74
+ </section>
75
+ </div>
76
+ </template>
@@ -0,0 +1,32 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "useDefineForClassFields": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "Bundler",
7
+ "baseUrl": ".",
8
+ "paths": {
9
+ "@orderlyshop/core-client": [
10
+ "../../../core-client/src/index.ts"
11
+ ],
12
+ "@orderlyshop/core-client/*": [
13
+ "../../../core-client/src/*"
14
+ ],
15
+ "@orderlyshop/web-components": [
16
+ "../../src/index.ts"
17
+ ],
18
+ "@orderlyshop/web-components/*": [
19
+ "../../src/*.ts"
20
+ ]
21
+ },
22
+ "strict": true,
23
+ "noEmit": true,
24
+ "skipLibCheck": true,
25
+ "types": [
26
+ "vitest/globals"
27
+ ]
28
+ },
29
+ "include": [
30
+ "src/**/*.ts"
31
+ ]
32
+ }
@@ -0,0 +1,190 @@
1
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
2
+ import { dirname, relative, resolve } from "node:path";
3
+ import { defineConfig } from "vite";
4
+
5
+ export default defineConfig({
6
+ server: {
7
+ host: "localhost",
8
+ port: 61677,
9
+ strictPort: true
10
+ },
11
+ preview: {
12
+ host: "localhost",
13
+ port: 61677,
14
+ strictPort: true
15
+ },
16
+ resolve: {
17
+ alias: sourceAliases()
18
+ },
19
+ plugins: [htmlIncludes(), sourceHtmlDevServer(), rootHtmlOutput()],
20
+ build: {
21
+ rollupOptions: {
22
+ input: htmlInputs()
23
+ }
24
+ }
25
+ });
26
+
27
+ function sourceAliases() {
28
+ const webComponentsSource = resolve(process.cwd(), "../../src/index.ts");
29
+ const coreClientSource = resolve(process.cwd(), "../../../core-client/src/index.ts");
30
+ const coreClientWebSource = resolve(process.cwd(), "../../../core-client/src/web.ts");
31
+ if (!existsSync(webComponentsSource)) {
32
+ return {};
33
+ }
34
+ return [
35
+ { find: /^@orderlyshop\/core-client\/web$/, replacement: coreClientWebSource },
36
+ { find: /^@orderlyshop\/core-client$/, replacement: coreClientSource },
37
+ { find: "@orderlyshop/web-components", replacement: webComponentsSource }
38
+ ];
39
+ }
40
+
41
+ function htmlIncludes() {
42
+ return {
43
+ name: "orderly-html-includes",
44
+ transformIndexHtml: {
45
+ order: "pre",
46
+ handler(html) {
47
+ return html
48
+ .replace("<!-- orderly-head-includes -->", include("head.html"))
49
+ .replace("<!-- orderly-body-start-includes -->", include("body-start.html"))
50
+ .replace("<!-- orderly-body-end-includes -->", include("body-end.html"));
51
+ }
52
+ }
53
+ };
54
+ }
55
+
56
+ function sourceHtmlDevServer() {
57
+ return {
58
+ name: "orderly-source-html-dev-server",
59
+ configureServer(server) {
60
+ server.middlewares.use(async (request, response, next) => {
61
+ const file = sourceHtmlFileForUrl(request.url ?? "/");
62
+ if (!file) {
63
+ next();
64
+ return;
65
+ }
66
+
67
+ try {
68
+ const html = await server.transformIndexHtml(request.url ?? "/", readFileSync(file, "utf8"));
69
+ response.statusCode = 200;
70
+ response.setHeader("Content-Type", "text/html");
71
+ response.end(html);
72
+ } catch (error) {
73
+ next(error);
74
+ }
75
+ });
76
+ }
77
+ };
78
+ }
79
+
80
+ function rootHtmlOutput() {
81
+ return {
82
+ name: "orderly-root-html-output",
83
+ enforce: "post",
84
+ generateBundle(_, bundle) {
85
+ for (const [fileName, output] of Object.entries(bundle)) {
86
+ if (!fileName.startsWith("src/") || !fileName.endsWith(".html")) {
87
+ continue;
88
+ }
89
+ delete bundle[fileName];
90
+ output.fileName = fileName.slice("src/".length);
91
+ bundle[output.fileName] = output;
92
+ }
93
+ }
94
+ };
95
+ }
96
+
97
+ function include(fileName) {
98
+ return includeFile(resolve(process.cwd(), "src/includes", fileName));
99
+ }
100
+
101
+ function includeFile(filePath) {
102
+ return expandTemplateIncludes(readFileSync(filePath, "utf8").trim(), dirname(filePath));
103
+ }
104
+
105
+ function expandTemplateIncludes(html, baseDir) {
106
+ return html.replace(/<!--\s*orderly-include-template:\s*([^>]+?)\s*-->/g, (_, templatePath) => {
107
+ return includeFile(resolve(baseDir, templatePath.trim()));
108
+ });
109
+ }
110
+
111
+ function htmlInputs(root = process.cwd()) {
112
+ const inputs = {};
113
+ for (const file of listHtmlFiles(root)) {
114
+ const name = htmlInputName(root, file);
115
+ inputs[name] = file;
116
+ }
117
+ return inputs;
118
+ }
119
+
120
+ function htmlInputName(root, file) {
121
+ const name = relative(root, file).replace(/\\/g, "/").replace(/\.html$/, "");
122
+ return name.startsWith("src/") ? name.slice("src/".length) : name;
123
+ }
124
+
125
+ function listHtmlFiles(dir) {
126
+ const files = [];
127
+ for (const entry of readdirSync(dir)) {
128
+ if (entry === "dist" || entry === "node_modules") {
129
+ continue;
130
+ }
131
+ const fullPath = resolve(dir, entry);
132
+ if (isHtmlPartial(fullPath)) {
133
+ continue;
134
+ }
135
+ const stat = statSync(fullPath);
136
+ if (stat.isDirectory()) {
137
+ files.push(...listHtmlFiles(fullPath));
138
+ } else if (entry.endsWith(".html")) {
139
+ files.push(fullPath);
140
+ }
141
+ }
142
+ return files;
143
+ }
144
+
145
+ function isHtmlPartial(file) {
146
+ const path = relative(process.cwd(), file);
147
+ return path.startsWith("src/includes") || path.startsWith("src/templates");
148
+ }
149
+
150
+ function sourceHtmlFileForUrl(url) {
151
+ const requestPath = safeHtmlRequestPath(url);
152
+ if (!requestPath) {
153
+ return undefined;
154
+ }
155
+ const categoryTemplate = resolve(process.cwd(), "src/category.html");
156
+ if (requestPath.startsWith("categories/") && requestPath.endsWith("index.html") && existsSync(categoryTemplate)) {
157
+ return categoryTemplate;
158
+ }
159
+ const file = resolve(process.cwd(), "src", requestPath);
160
+ if (!file.startsWith(resolve(process.cwd(), "src")) || isHtmlPartial(file) || !existsSync(file)) {
161
+ return undefined;
162
+ }
163
+ return statSync(file).isFile() ? file : undefined;
164
+ }
165
+
166
+ function safeHtmlRequestPath(url) {
167
+ const [rawPath] = url.split(/[?#]/, 1);
168
+ let path;
169
+ try {
170
+ path = decodeURIComponent(rawPath || "/");
171
+ } catch {
172
+ return undefined;
173
+ }
174
+ if (path.includes("..")) {
175
+ return undefined;
176
+ }
177
+ if (path === "/") {
178
+ return "index.html";
179
+ }
180
+ if (path.endsWith("/")) {
181
+ return `${path.slice(1)}index.html`;
182
+ }
183
+ if (path.startsWith("/categories/") && !path.includes(".")) {
184
+ return `${path.slice(1)}/index.html`;
185
+ }
186
+ if (path.endsWith(".html")) {
187
+ return path.slice(1);
188
+ }
189
+ return undefined;
190
+ }