@salla.sa/twilight-bundles-starter-kit 0.1.1

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 ADDED
@@ -0,0 +1,184 @@
1
+ # Salla Twilight Bundles Starter Kit
2
+
3
+ This starter kit provides a foundation for building custom Twilight components for Salla's e-commerce platform. It includes a pre-configured build setup and development environment to help you get started quickly.
4
+
5
+ ## Getting Started
6
+
7
+ 1. Clone this repository
8
+ 2. Remove the example components in `src/components/`
9
+ 3. Create your own components using the component generator:
10
+ ```
11
+ pnpm run create-component
12
+ ```
13
+ 4. Run `pnpm install` to install dependencies
14
+ 5. Run `pnpm run dev` to start the development server
15
+ 6. Run `pnpm run build` to build your components for production
16
+
17
+ ## Project Structure
18
+
19
+ ```
20
+ src/
21
+ components/
22
+ your-component-name/
23
+ index.ts # Main component file
24
+ styles.ts # Component styles (optional)
25
+ types.ts # Component types (optional)
26
+ ```
27
+
28
+ ## Built-in Plugins
29
+
30
+ This starter kit includes three Vite plugins that handle the build process:
31
+
32
+ ### 1. Transform Plugin (`sallaTransformPlugin`)
33
+ - Transforms component files to ensure proper naming and registration
34
+ - Matches components in `src/components/*/index.ts`
35
+ - To disable: Remove from `vite.config.ts` plugins array
36
+
37
+ ### 2. Build Plugin (`sallaBuildPlugin`)
38
+ - Handles component bundling and output
39
+ - Creates individual files for each component in `dist/`
40
+ - Configures external dependencies (lit libraries)
41
+ - To customize: Remove from plugins array and configure your own build settings:
42
+ ```typescript
43
+ {
44
+ build: {
45
+ lib: {
46
+ entry: {/* your entries */},
47
+ formats: ['es'],
48
+ fileName: (format, entryName) => `${entryName}.js`
49
+ },
50
+ rollupOptions: {
51
+ external: [/^lit/],
52
+ output: {/* your output config */}
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ ### 3. Demo Plugin (`sallaDemoPlugin`)
59
+ - Provides a development environment for testing components
60
+ - Creates a demo page with your components
61
+ - Configures hot module reloading
62
+ - To disable: Remove from plugins array and set up your own dev server
63
+
64
+ ### Demo Plugin Options
65
+
66
+ The `sallaDemoPlugin` accepts the following configuration options:
67
+
68
+ ```typescript
69
+ {
70
+ // Optional: Show only specific components
71
+ components?: string[];
72
+
73
+ // Optional: Customize the demo grid layout
74
+ grid?: {
75
+ // CSS grid-template-columns value
76
+ columns?: string; // default: 'repeat(auto-fill, minmax(300px, 1fr))'
77
+
78
+ // Gap between components
79
+ gap?: string; // default: '1rem'
80
+
81
+ // Responsive breakpoint
82
+ minWidth?: string; // default: '300px'
83
+ };
84
+
85
+ // Optional: Add custom CSS
86
+ css?: string;
87
+
88
+ // Optional: Add custom JavaScript
89
+ js?: string;
90
+ }
91
+ ```
92
+
93
+ #### Example Configuration
94
+
95
+ ```typescript
96
+ // vite.config.ts
97
+ export default defineConfig({
98
+ plugins: [
99
+ // ... other plugins
100
+ sallaDemoPlugin({
101
+ // Show only specific components
102
+ components: ['product-card', 'scroll-top'],
103
+
104
+ // Customize grid layout
105
+ grid: {
106
+ columns: 'repeat(3, 1fr)',
107
+ gap: '1.5rem',
108
+ minWidth: '768px'
109
+ },
110
+
111
+ // Add custom styles
112
+ css: `
113
+ .component-card {
114
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
115
+ transition: transform 0.2s;
116
+ }
117
+ .component-card:hover {
118
+ transform: translateY(-2px);
119
+ }
120
+ `,
121
+
122
+ // Add custom JavaScript
123
+ js: `
124
+ console.log('Demo page loaded!');
125
+ // Add your custom JavaScript here
126
+ `
127
+ })
128
+ ]
129
+ });
130
+ ```
131
+
132
+ ## Creating New Components
133
+
134
+ This starter kit includes a component generator to help you create new components quickly. To use it, run:
135
+
136
+ ```bash
137
+ pnpm create-component
138
+ ```
139
+
140
+ The generator will:
141
+ 1. Prompt you for a component name (in kebab-case format)
142
+ 2. Validate that the name is in kebab-case and doesn't already exist
143
+ 3. Create a new component folder with an `index.ts` file
144
+ 4. Add the component definition to `twilight-bundle.json`
145
+
146
+ ## Component Requirements
147
+
148
+ Each component should:
149
+ 1. Be a class that extends `LitElement`
150
+ 2. Export the class as default
151
+ 3. Be placed in its own directory under `src/components/`
152
+ 4. Have an `index.ts` as the entry point
153
+
154
+ Example:
155
+ ```typescript
156
+ import { css, html, LitElement } from 'lit';
157
+ import { property } from 'lit/decorators.js';
158
+
159
+ export default class MyComponent extends LitElement {
160
+ @property({ type: String })
161
+ name = 'World';
162
+
163
+ static styles = css`/* your styles */`;
164
+
165
+ render() {
166
+ return html`<div>Hello ${this.name}!</div>`;
167
+ }
168
+ }
169
+ ```
170
+
171
+ ## Building for Production
172
+
173
+ Run `pnpm run build` to create production-ready bundles in the `dist/` directory. Each component will have its own file named after the component (e.g., `my-component.js`).
174
+
175
+ ## Development
176
+
177
+ Run `pnpm run dev` to start the development server. This will:
178
+ 1. Create a demo page with all your components
179
+ 2. Enable hot module reloading
180
+ 3. Provide a development environment for testing
181
+
182
+ ## License
183
+
184
+ MIT
@@ -0,0 +1,54 @@
1
+ import { LitElement as a, css as f, html as d } from "lit";
2
+ import { property as p } from "lit/decorators.js";
3
+ var c = Object.defineProperty, m = (s, r, e, g) => {
4
+ for (var i = void 0, o = s.length - 1, n; o >= 0; o--)
5
+ (n = s[o]) && (i = n(r, e, i) || i);
6
+ return i && c(r, e, i), i;
7
+ };
8
+ const l = class l extends a {
9
+ render() {
10
+ return this.config ? d`
11
+ <div class="grid">
12
+ ${Object.entries(this.config).map(([r, e]) => this.renderValue(r, e))}
13
+ </div>
14
+ ` : d`<div style="color: red;">Configuration is required</div>`;
15
+ }
16
+ renderValue(r, e) {
17
+ return typeof e == "object" && (e = JSON.stringify(e)), d`
18
+ <div class="label">${r}</div>
19
+ <code class="value">${e}</code>
20
+ `;
21
+ }
22
+ };
23
+ l.styles = f`
24
+ .grid {
25
+ display: grid;
26
+ grid-template-columns: 20% 75%;
27
+ gap: 0.5rem 1rem;
28
+ align-items: center;
29
+ }
30
+ .label {
31
+ font-weight: bold;
32
+ font-size: 0.9rem;
33
+ text-align: left;
34
+ }
35
+ .value {
36
+ background: #f5f5f5;
37
+ padding: 0.5rem;
38
+ border: 1px solid #ddd;
39
+ border-radius: 4px;
40
+ font-family: monospace;
41
+ white-space: nowrap;
42
+ overflow: hidden;
43
+ text-overflow: ellipsis;
44
+ direction: ltr;
45
+ }
46
+ `;
47
+ let t = l;
48
+ m([
49
+ p({ type: Object })
50
+ ], t.prototype, "config");
51
+ typeof t < "u" && t.registerSallaComponent("salla-advanced-inputs");
52
+ export {
53
+ t as default
54
+ };
@@ -0,0 +1,54 @@
1
+ import { LitElement as a, css as f, html as d } from "lit";
2
+ import { property as p } from "lit/decorators.js";
3
+ var c = Object.defineProperty, m = (n, r, e, g) => {
4
+ for (var i = void 0, o = n.length - 1, s; o >= 0; o--)
5
+ (s = n[o]) && (i = s(r, e, i) || i);
6
+ return i && c(r, e, i), i;
7
+ };
8
+ const l = class l extends a {
9
+ render() {
10
+ return this.config ? d`
11
+ <div class="grid">
12
+ ${Object.entries(this.config).map(([r, e]) => this.renderValue(r, e))}
13
+ </div>
14
+ ` : d`<div style="color: red;">Configuration is required</div>`;
15
+ }
16
+ renderValue(r, e) {
17
+ return typeof e == "object" && (e = JSON.stringify(e)), d`
18
+ <div class="label">${r}</div>
19
+ <code class="value">${e}</code>
20
+ `;
21
+ }
22
+ };
23
+ l.styles = f`
24
+ .grid {
25
+ display: grid;
26
+ grid-template-columns: 20% 75%;
27
+ gap: 0.5rem 1rem;
28
+ align-items: center;
29
+ }
30
+ .label {
31
+ font-weight: bold;
32
+ font-size: 0.9rem;
33
+ text-align: left;
34
+ }
35
+ .value {
36
+ background: #f5f5f5;
37
+ padding: 0.5rem;
38
+ border: 1px solid #ddd;
39
+ border-radius: 4px;
40
+ font-family: monospace;
41
+ white-space: nowrap;
42
+ overflow: hidden;
43
+ text-overflow: ellipsis;
44
+ direction: ltr;
45
+ }
46
+ `;
47
+ let t = l;
48
+ m([
49
+ p({ type: Object })
50
+ ], t.prototype, "config");
51
+ typeof t < "u" && t.registerSallaComponent("salla-basic-inputs");
52
+ export {
53
+ t as default
54
+ };
@@ -0,0 +1,54 @@
1
+ import { LitElement as f, css as p, html as l } from "lit";
2
+ import { property as c } from "lit/decorators.js";
3
+ var m = Object.defineProperty, g = (s, r, e, n) => {
4
+ for (var i = void 0, d = s.length - 1, a; d >= 0; d--)
5
+ (a = s[d]) && (i = a(r, e, i) || i);
6
+ return i && m(r, e, i), i;
7
+ };
8
+ const o = class o extends f {
9
+ render() {
10
+ return this.config ? l`
11
+ <div class="grid">
12
+ ${Object.entries(this.config).map(([r, e]) => this.renderValue(r, e))}
13
+ </div>
14
+ ` : l`<div style="color: red;">Configuration is required</div>`;
15
+ }
16
+ renderValue(r, e) {
17
+ return e = typeof e == "object" ? e.map((n) => n.label).join(", ") : e, l`
18
+ <div class="label">${r}</div>
19
+ <code class="value">${e}</code>
20
+ `;
21
+ }
22
+ };
23
+ o.styles = p`
24
+ .grid {
25
+ display: grid;
26
+ grid-template-columns: 30% 65%;
27
+ gap: 0.5rem 1rem;
28
+ align-items: center;
29
+ }
30
+ .label {
31
+ font-weight: bold;
32
+ font-size: 0.9rem;
33
+ text-align: left;
34
+ }
35
+ .value {
36
+ background: #f5f5f5;
37
+ padding: 0.5rem;
38
+ border: 1px solid #ddd;
39
+ border-radius: 4px;
40
+ font-family: monospace;
41
+ white-space: nowrap;
42
+ overflow: hidden;
43
+ text-overflow: ellipsis;
44
+ direction: ltr;
45
+ }
46
+ `;
47
+ let t = o;
48
+ g([
49
+ c({ type: Object })
50
+ ], t.prototype, "config");
51
+ typeof t < "u" && t.registerSallaComponent("salla-dropdown-list-source-input");
52
+ export {
53
+ t as default
54
+ };
@@ -0,0 +1,51 @@
1
+ import { LitElement as f, css as p, html as d } from "lit";
2
+ import { property as c } from "lit/decorators.js";
3
+ var m = Object.defineProperty, g = (s, r, e, n) => {
4
+ for (var i = void 0, o = s.length - 1, a; o >= 0; o--)
5
+ (a = s[o]) && (i = a(r, e, i) || i);
6
+ return i && m(r, e, i), i;
7
+ };
8
+ const l = class l extends f {
9
+ render() {
10
+ return this.config ? d`
11
+ <div class="grid">
12
+ ${Object.entries(this.config).map(([r, e]) => this.renderValue(r, e))}
13
+ </div>
14
+ ` : d`<div style="color: red;">Configuration is required</div>`;
15
+ }
16
+ renderValue(r, e) {
17
+ return e = typeof e == "object" ? e.map((n) => n.label).join(", ") : e, d`<div class="label">${r}</div><code class="value">${e}</code>`;
18
+ }
19
+ };
20
+ l.styles = p`
21
+ .grid {
22
+ display: grid;
23
+ grid-template-columns: 35% 60%;
24
+ gap: 0.5rem 1rem;
25
+ align-items: center;
26
+ }
27
+ .label {
28
+ font-weight: bold;
29
+ font-size: 0.9rem;
30
+ text-align: left;
31
+ }
32
+ .value {
33
+ background: #f5f5f5;
34
+ padding: 0.5rem;
35
+ border: 1px solid #ddd;
36
+ border-radius: 4px;
37
+ font-family: monospace;
38
+ white-space: nowrap;
39
+ overflow: hidden;
40
+ text-overflow: ellipsis;
41
+ direction: ltr;
42
+ }
43
+ `;
44
+ let t = l;
45
+ g([
46
+ c({ type: Object })
47
+ ], t.prototype, "config");
48
+ typeof t < "u" && t.registerSallaComponent("salla-items-select-input");
49
+ export {
50
+ t as default
51
+ };
@@ -0,0 +1,160 @@
1
+ import { LitElement as u, css as m, html as d } from "lit";
2
+ import { property as l } from "lit/decorators.js";
3
+ var g = Object.defineProperty, p = (s, t, r, a) => {
4
+ for (var e = void 0, i = s.length - 1, c; i >= 0; i--)
5
+ (c = s[i]) && (e = c(t, r, e) || e);
6
+ return e && g(t, r, e), e;
7
+ };
8
+ const n = class n extends u {
9
+ async connectedCallback() {
10
+ var r, a, e;
11
+ super.connectedCallback();
12
+ const t = (e = (a = (r = this.config) == null ? void 0 : r.product) == null ? void 0 : a[0]) == null ? void 0 : e.value;
13
+ if (!t) {
14
+ console.error('Product card config is not valid, you must provide `config="{...}"!');
15
+ return;
16
+ }
17
+ await window.Salla.onReady(), this.product = await window.Salla.product.api.getDetails(t).then((i) => i.data);
18
+ }
19
+ handleAddToCart() {
20
+ var t;
21
+ window.Salla.log("Adding to cart:", { product: this.product }), window.Salla.success(`Added ${(t = this.product) == null ? void 0 : t.name} to cart!`);
22
+ }
23
+ renderPlaceholder() {
24
+ return d`
25
+ <div class="product-card">
26
+ <div class="skeleton-image"></div>
27
+ <div class="skeleton-title"></div>
28
+ <div class="skeleton-price"></div>
29
+ <div class="skeleton-button"></div>
30
+ </div>
31
+ `;
32
+ }
33
+ render() {
34
+ var t, r;
35
+ return this.product ? d`
36
+ <div class="product-card">
37
+ <img
38
+ class="product-image"
39
+ src="${(t = this.product.image) == null ? void 0 : t.url}"
40
+ alt="${(r = this.product.image) == null ? void 0 : r.alt}"
41
+ />
42
+ <h3 class="product-title">${this.product.name}</h3>
43
+ <div>
44
+ <span class="price-tag">${window.Salla.money(this.product.price)}</span>
45
+ ${this.product.discount ? d`<span class="discount">${window.Salla.money(this.product.discount)}</span>` : ""}
46
+ </div>
47
+ <button class="add-to-cart" @click="${this.handleAddToCart}">
48
+ ${window.Salla.lang.get("pages.cart.add_to_cart")}
49
+ </button>
50
+ </div>
51
+ ` : this.renderPlaceholder();
52
+ }
53
+ };
54
+ n.styles = m`
55
+ .product-card {
56
+ width: 250px;
57
+ padding: 1rem;
58
+ border-radius: 8px;
59
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
60
+ transition: transform 0.2s;
61
+ background: white;
62
+ margin: 1rem;
63
+ }
64
+ .product-card:hover {
65
+ transform: translateY(-5px);
66
+ }
67
+ .product-image {
68
+ width: 100%;
69
+ height: 200px;
70
+ object-fit: cover;
71
+ border-radius: 4px;
72
+ }
73
+ .product-title {
74
+ margin: 0.5rem 0;
75
+ color: #333;
76
+ font-size: 1.1rem;
77
+ }
78
+ .price-tag {
79
+ font-size: 1.2rem;
80
+ color:var(--primary);
81
+ font-weight: bold;
82
+ }
83
+ .discount {
84
+ background: #ff4444;
85
+ color: white;
86
+ padding: 0.2rem 0.5rem;
87
+ border-radius: 4px;
88
+ font-size: 0.9rem;
89
+ margin-left: 0.5rem;
90
+ }
91
+ .add-to-cart {
92
+ width: 100%;
93
+ padding: 0.8rem;
94
+ margin-top: 1rem;
95
+ border: none;
96
+ border-radius: 4px;
97
+ background: var(--primary);
98
+ color: white;
99
+ font-weight: bold;
100
+ cursor: pointer;
101
+ transition: background 0.2s;
102
+ }
103
+ .add-to-cart:hover {
104
+ background: var(--primary-100);
105
+ }
106
+
107
+ /* Skeleton loading styles */
108
+ @keyframes pulse {
109
+ 0% { opacity: 0.6; }
110
+ 50% { opacity: 0.8; }
111
+ 100% { opacity: 0.6; }
112
+ }
113
+
114
+ .skeleton-image {
115
+ width: 100%;
116
+ height: 200px;
117
+ background-color: #e0e0e0;
118
+ border-radius: 4px;
119
+ margin-bottom: 0.5rem;
120
+ animation: pulse 1.5s infinite ease-in-out;
121
+ }
122
+
123
+ .skeleton-title {
124
+ width: 80%;
125
+ height: 20px;
126
+ background-color: #e0e0e0;
127
+ border-radius: 4px;
128
+ margin: 0.5rem 0;
129
+ animation: pulse 1.5s infinite ease-in-out;
130
+ }
131
+
132
+ .skeleton-price {
133
+ width: 40%;
134
+ height: 24px;
135
+ background-color: #e0e0e0;
136
+ border-radius: 4px;
137
+ margin: 0.5rem 0;
138
+ animation: pulse 1.5s infinite ease-in-out;
139
+ }
140
+
141
+ .skeleton-button {
142
+ width: 100%;
143
+ height: 40px;
144
+ background-color: #e0e0e0;
145
+ border-radius: 4px;
146
+ margin-top: 1rem;
147
+ animation: pulse 1.5s infinite ease-in-out;
148
+ }
149
+ `;
150
+ let o = n;
151
+ p([
152
+ l({ type: Object })
153
+ ], o.prototype, "config");
154
+ p([
155
+ l({ type: Object })
156
+ ], o.prototype, "product");
157
+ typeof o < "u" && o.registerSallaComponent("salla-product-card");
158
+ export {
159
+ o as default
160
+ };
@@ -0,0 +1,20 @@
1
+ class e extends HTMLElement {
2
+ connectedCallback() {
3
+ this.innerHTML = "↑", this.style.cssText = `
4
+ border-radius: 50%;
5
+ display: flex;
6
+ justify-content: center;
7
+ align-items: center;
8
+ width: 40px;
9
+ height: 40px;
10
+ background: hsl(var(--primary-400));
11
+ color: hsl(var(--primary-force));
12
+ cursor:pointer;
13
+ font-size: 24px;
14
+ `, this.onclick = () => window.scrollTo(0, 0);
15
+ }
16
+ }
17
+ typeof e < "u" && e.registerSallaComponent("salla-scroll-top");
18
+ export {
19
+ e as default
20
+ };
@@ -0,0 +1,66 @@
1
+ import { LitElement as p, css as c, html as d } from "lit";
2
+ import { property as m } from "lit/decorators.js";
3
+ var f = Object.defineProperty, b = (a, t, i, o) => {
4
+ for (var e = void 0, s = a.length - 1, n; s >= 0; s--)
5
+ (n = a[s]) && (e = n(t, i, e) || e);
6
+ return e && f(t, i, e), e;
7
+ };
8
+ const l = class l extends p {
9
+ render() {
10
+ var t, i;
11
+ return d`
12
+ <div class="table-list">
13
+ ${(i = (t = this.config) == null ? void 0 : t.items) == null ? void 0 : i.map(
14
+ (o) => d`
15
+ <div class="table-item">
16
+ <div class="item-content">
17
+ <h3 class="item-title">${o.title}</h3>
18
+ <p class="item-description">${o.description}</p>
19
+ </div>
20
+ </div>
21
+ `
22
+ )}
23
+ </div>
24
+ `;
25
+ }
26
+ };
27
+ l.styles = c`
28
+ .table-list {
29
+ width: 100%;
30
+ border-radius: 8px;
31
+ overflow: hidden;
32
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
33
+ }
34
+ .table-item {
35
+ display: flex;
36
+ padding: 1rem;
37
+ background: white;
38
+ border-bottom: 1px solid #eee;
39
+ align-items: center;
40
+ transition: background 0.2s;
41
+ }
42
+ .table-item:hover {
43
+ background: #f8f9fa;
44
+ }
45
+ .item-content {
46
+ flex: 1;
47
+ }
48
+ .item-title {
49
+ font-weight: 500;
50
+ color: #2c3e50;
51
+ margin: 0;
52
+ }
53
+ .item-description {
54
+ color: #666;
55
+ font-size: 0.9rem;
56
+ margin: 4px 0 0;
57
+ }
58
+ `;
59
+ let r = l;
60
+ b([
61
+ m({ type: Object })
62
+ ], r.prototype, "config");
63
+ typeof r < "u" && r.registerSallaComponent("salla-table-list");
64
+ export {
65
+ r as default
66
+ };
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@salla.sa/twilight-bundles-starter-kit",
3
+ "version": "0.1.1",
4
+ "description": "Starter kit for building custom Salla components",
5
+ "type": "module",
6
+ "keywords": [
7
+ "salla",
8
+ "ecommerce",
9
+ "components",
10
+ "web-components",
11
+ "lit-element"
12
+ ],
13
+ "author": "Salla",
14
+ "license": "MIT",
15
+ "devDependencies": {
16
+ "@testing-library/dom": "^9.3.4",
17
+ "@testing-library/jest-dom": "^6.4.2",
18
+ "@types/node": "^22.13.1",
19
+ "@typescript-eslint/eslint-plugin": "^8.24.0",
20
+ "@typescript-eslint/parser": "^8.24.0",
21
+ "esbuild": "^0.25.0",
22
+ "eslint": "^9.20.1",
23
+ "glob": "^11.0.1",
24
+ "jsdom": "^24.0.0",
25
+ "prettier": "^3.5.0",
26
+ "typescript": "^5.7.3",
27
+ "vite": "^6.1.0",
28
+ "vitest": "^3.0.5"
29
+ },
30
+ "dependencies": {
31
+ "lit": "^3.2.1",
32
+ "@salla.sa/twilight-bundles": "0.1.1"
33
+ },
34
+ "engines": {
35
+ "node": ">=16.0.0",
36
+ "pnpm": ">=9.0.0"
37
+ },
38
+ "scripts": {
39
+ "dev": "vite",
40
+ "build": "vite build",
41
+ "preview": "vite preview",
42
+ "format": "prettier --write \"src/**/*.{ts,html,css}\"",
43
+ "create-component": "tw-component"
44
+ }
45
+ }