@papu1337/builder 0.0.3
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 +65 -0
- package/dist/elements/auth/authElement.svelte +115 -0
- package/dist/elements/auth/authElement.svelte.d.ts +7 -0
- package/dist/elements/auth/settings.d.ts +25 -0
- package/dist/elements/auth/settings.js +63 -0
- package/dist/elements/badge/badgeElement.svelte +48 -0
- package/dist/elements/badge/badgeElement.svelte.d.ts +7 -0
- package/dist/elements/badge/settings.d.ts +13 -0
- package/dist/elements/badge/settings.js +57 -0
- package/dist/elements/banner/bannerElement.svelte +46 -0
- package/dist/elements/banner/bannerElement.svelte.d.ts +7 -0
- package/dist/elements/banner/settings.d.ts +6 -0
- package/dist/elements/banner/settings.js +15 -0
- package/dist/elements/button/buttonElement.svelte +42 -0
- package/dist/elements/button/buttonElement.svelte.d.ts +7 -0
- package/dist/elements/button/settings.d.ts +16 -0
- package/dist/elements/button/settings.js +43 -0
- package/dist/elements/cards/cardsElement.svelte +136 -0
- package/dist/elements/cards/cardsElement.svelte.d.ts +7 -0
- package/dist/elements/cards/settings.d.ts +14 -0
- package/dist/elements/cards/settings.js +52 -0
- package/dist/elements/divider/dividerElement.svelte +34 -0
- package/dist/elements/divider/dividerElement.svelte.d.ts +7 -0
- package/dist/elements/divider/settings.d.ts +7 -0
- package/dist/elements/divider/settings.js +15 -0
- package/dist/elements/globalSettings.d.ts +8 -0
- package/dist/elements/globalSettings.js +39 -0
- package/dist/elements/products/productsElement.svelte +283 -0
- package/dist/elements/products/productsElement.svelte.d.ts +7 -0
- package/dist/elements/products/settings.d.ts +16 -0
- package/dist/elements/products/settings.js +56 -0
- package/dist/elements/terms/settings.d.ts +11 -0
- package/dist/elements/terms/settings.js +39 -0
- package/dist/elements/terms/termsElement.svelte +124 -0
- package/dist/elements/terms/termsElement.svelte.d.ts +7 -0
- package/dist/elements/text/settings.d.ts +11 -0
- package/dist/elements/text/settings.js +12 -0
- package/dist/elements/text/textElement.svelte +33 -0
- package/dist/elements/text/textElement.svelte.d.ts +7 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/renderer/BuilderRenderer.svelte +38 -0
- package/dist/renderer/BuilderRenderer.svelte.d.ts +4 -0
- package/dist/renderer/index.d.ts +4 -0
- package/dist/renderer/index.js +3 -0
- package/dist/renderer/registry.d.ts +4 -0
- package/dist/renderer/registry.js +20 -0
- package/dist/renderer/renderer.vanilla.es.js +2835 -0
- package/dist/renderer/renderer.vanilla.umd.js +31 -0
- package/dist/renderer/resolve.d.ts +1 -0
- package/dist/renderer/resolve.js +30 -0
- package/dist/renderer/types.d.ts +19 -0
- package/dist/renderer/types.js +1 -0
- package/dist/renderer/vanilla.svelte.d.ts +2 -0
- package/dist/renderer/vanilla.svelte.js +28 -0
- package/dist/settings/base.svelte.d.ts +30 -0
- package/dist/settings/base.svelte.js +91 -0
- package/dist/settings/components/ColorSettings.svelte +141 -0
- package/dist/settings/components/ColorSettings.svelte.d.ts +4 -0
- package/dist/settings/components/ListSettings.svelte +167 -0
- package/dist/settings/components/ListSettings.svelte.d.ts +14 -0
- package/dist/settings/components/NumberSettings.svelte +67 -0
- package/dist/settings/components/NumberSettings.svelte.d.ts +5 -0
- package/dist/settings/components/SelectSettings.svelte +64 -0
- package/dist/settings/components/SelectSettings.svelte.d.ts +5 -0
- package/dist/settings/components/SettingsGroup.svelte +94 -0
- package/dist/settings/components/SettingsGroup.svelte.d.ts +9 -0
- package/dist/settings/components/TextSettings.svelte +8 -0
- package/dist/settings/components/TextSettings.svelte.d.ts +4 -0
- package/dist/settings/components/TranslatableSettings.svelte +208 -0
- package/dist/settings/components/TranslatableSettings.svelte.d.ts +8 -0
- package/dist/settings/components/UploadSettings.svelte +185 -0
- package/dist/settings/components/UploadSettings.svelte.d.ts +4 -0
- package/dist/settings/groups.d.ts +14 -0
- package/dist/settings/groups.js +35 -0
- package/dist/settings/implementation.svelte.js +41 -0
- package/dist/settings/index.d.ts +4 -0
- package/dist/settings/index.js +4 -0
- package/dist/settings/types.d.ts +51 -0
- package/dist/settings/types.js +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { CardsSettingsMap } from './settings';
|
|
3
|
+
|
|
4
|
+
let { settings }: { settings: CardsSettingsMap } = $props();
|
|
5
|
+
|
|
6
|
+
const rows = $derived(() => {
|
|
7
|
+
const all = settings.cards;
|
|
8
|
+
const chunks: (typeof all)[] = [];
|
|
9
|
+
for (let i = 0; i < all.length; i += 3) {
|
|
10
|
+
chunks.push(all.slice(i, i + 3));
|
|
11
|
+
}
|
|
12
|
+
return chunks;
|
|
13
|
+
});
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<div class="cards-wrapper" style="margin-top: {settings.marginTop}px;">
|
|
17
|
+
{#each rows() as row}
|
|
18
|
+
<div class="cards-row">
|
|
19
|
+
{#each row as card, i}
|
|
20
|
+
{#if i > 0}
|
|
21
|
+
<div class="arrow" style="color: {settings.arrowColor};">
|
|
22
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
|
|
23
|
+
<path
|
|
24
|
+
d="M9 6l6 6-6 6"
|
|
25
|
+
stroke="currentColor"
|
|
26
|
+
stroke-width="2"
|
|
27
|
+
stroke-linecap="round"
|
|
28
|
+
stroke-linejoin="round"
|
|
29
|
+
/>
|
|
30
|
+
</svg>
|
|
31
|
+
</div>
|
|
32
|
+
{/if}
|
|
33
|
+
<div
|
|
34
|
+
class="card"
|
|
35
|
+
style="background: {settings.backgroundColor}; border: 1px solid {settings.borderColor};"
|
|
36
|
+
>
|
|
37
|
+
<h3 class="card-title" style="color: {settings.titleColor};">
|
|
38
|
+
{card.title}
|
|
39
|
+
</h3>
|
|
40
|
+
<p class="card-content" style="color: {settings.contentColor};">
|
|
41
|
+
{card.content}
|
|
42
|
+
</p>
|
|
43
|
+
</div>
|
|
44
|
+
{/each}
|
|
45
|
+
</div>
|
|
46
|
+
{/each}
|
|
47
|
+
|
|
48
|
+
{#if settings.footerText}
|
|
49
|
+
<div
|
|
50
|
+
class="cards-footer"
|
|
51
|
+
style="background: {settings.footerBackground}; color: {settings.footerTextColor};"
|
|
52
|
+
>
|
|
53
|
+
<p>{settings.footerText}</p>
|
|
54
|
+
</div>
|
|
55
|
+
{/if}
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<style>
|
|
59
|
+
.cards-wrapper {
|
|
60
|
+
display: flex;
|
|
61
|
+
flex-direction: column;
|
|
62
|
+
gap: 12px;
|
|
63
|
+
max-width: 750px;
|
|
64
|
+
width: 100%;
|
|
65
|
+
margin: 0 auto;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.cards-row {
|
|
69
|
+
display: flex;
|
|
70
|
+
align-items: stretch;
|
|
71
|
+
gap: 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.arrow {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
justify-content: center;
|
|
78
|
+
flex-shrink: 0;
|
|
79
|
+
padding: 0 6px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.card {
|
|
83
|
+
flex: 1;
|
|
84
|
+
max-width: calc(33.333% - 12px);
|
|
85
|
+
border-radius: 10px;
|
|
86
|
+
padding: 24px 20px;
|
|
87
|
+
display: flex;
|
|
88
|
+
flex-direction: column;
|
|
89
|
+
align-items: center;
|
|
90
|
+
text-align: center;
|
|
91
|
+
gap: 10px;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.card-title {
|
|
95
|
+
margin: 0;
|
|
96
|
+
font-size: 0.95rem;
|
|
97
|
+
font-weight: 800;
|
|
98
|
+
text-transform: uppercase;
|
|
99
|
+
letter-spacing: 0.03em;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.card-content {
|
|
103
|
+
margin: 0;
|
|
104
|
+
font-size: 0.85rem;
|
|
105
|
+
line-height: 1.5;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.cards-footer {
|
|
109
|
+
border-radius: 8px;
|
|
110
|
+
padding: 14px 20px;
|
|
111
|
+
text-align: center;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.cards-footer p {
|
|
115
|
+
margin: 0;
|
|
116
|
+
font-size: 0.85rem;
|
|
117
|
+
line-height: 1.5;
|
|
118
|
+
font-style: italic;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@media (max-width: 640px) {
|
|
122
|
+
.cards-row {
|
|
123
|
+
flex-direction: column;
|
|
124
|
+
gap: 12px;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.card {
|
|
128
|
+
max-width: 100%;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.arrow {
|
|
132
|
+
transform: rotate(90deg);
|
|
133
|
+
padding: 4px 0;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
</style>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CardsSettingsMap } from './settings';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
settings: CardsSettingsMap;
|
|
4
|
+
};
|
|
5
|
+
declare const CardsElement: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
6
|
+
type CardsElement = ReturnType<typeof CardsElement>;
|
|
7
|
+
export default CardsElement;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ColorSetting, NumberSetting, TextSetting, ListSetting, Settings, type InferSettingsMapType } from '../../settings';
|
|
2
|
+
export declare const cardsSettings: Settings<{
|
|
3
|
+
marginTop: NumberSetting;
|
|
4
|
+
cards: ListSetting;
|
|
5
|
+
footerText: TextSetting;
|
|
6
|
+
backgroundColor: ColorSetting;
|
|
7
|
+
borderColor: ColorSetting;
|
|
8
|
+
titleColor: ColorSetting;
|
|
9
|
+
contentColor: ColorSetting;
|
|
10
|
+
footerBackground: ColorSetting;
|
|
11
|
+
footerTextColor: ColorSetting;
|
|
12
|
+
arrowColor: ColorSetting;
|
|
13
|
+
}>;
|
|
14
|
+
export type CardsSettingsMap = InferSettingsMapType<typeof cardsSettings>;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { ColorSetting, NumberSetting, TextSetting, ListSetting, Settings } from '../../settings';
|
|
2
|
+
export const cardsSettings = new Settings('Cards', {
|
|
3
|
+
marginTop: new NumberSetting({
|
|
4
|
+
title: 'Margin Top',
|
|
5
|
+
defaultValue: 0
|
|
6
|
+
}),
|
|
7
|
+
cards: new ListSetting({
|
|
8
|
+
title: 'Cards',
|
|
9
|
+
defaultValue: [
|
|
10
|
+
{ title: 'CREATE YOUR TICKET', content: 'Pick 3+ positions from UEL & UECL matches' },
|
|
11
|
+
{ title: 'PLACE YOUR BET', content: 'With Combo or Bet Builder ticket' },
|
|
12
|
+
{ title: 'GET 50% FREEBET', content: 'In case you lose any position' }
|
|
13
|
+
],
|
|
14
|
+
extra: {
|
|
15
|
+
addButtonText: '+ Add Card',
|
|
16
|
+
titlePlaceholder: 'Card title',
|
|
17
|
+
contentPlaceholder: 'Card description'
|
|
18
|
+
}
|
|
19
|
+
}),
|
|
20
|
+
footerText: new TextSetting({
|
|
21
|
+
title: 'Footer Text',
|
|
22
|
+
defaultValue: 'Example: Place a 3-leg combo for $200 and get 50% back as a $100 Freebet if your ticket loses.'
|
|
23
|
+
}),
|
|
24
|
+
backgroundColor: new ColorSetting({
|
|
25
|
+
title: 'Card Background',
|
|
26
|
+
defaultValue: 'rgba(22, 45, 63, 0.8)'
|
|
27
|
+
}),
|
|
28
|
+
borderColor: new ColorSetting({
|
|
29
|
+
title: 'Card Border Color',
|
|
30
|
+
defaultValue: 'rgba(177, 202, 223, 0.15)'
|
|
31
|
+
}),
|
|
32
|
+
titleColor: new ColorSetting({
|
|
33
|
+
title: 'Card Title Color',
|
|
34
|
+
defaultValue: '#ffffff'
|
|
35
|
+
}),
|
|
36
|
+
contentColor: new ColorSetting({
|
|
37
|
+
title: 'Card Content Color',
|
|
38
|
+
defaultValue: 'rgba(255, 255, 255, 0.7)'
|
|
39
|
+
}),
|
|
40
|
+
footerBackground: new ColorSetting({
|
|
41
|
+
title: 'Footer Background',
|
|
42
|
+
defaultValue: 'rgba(22, 45, 63, 0.6)'
|
|
43
|
+
}),
|
|
44
|
+
footerTextColor: new ColorSetting({
|
|
45
|
+
title: 'Footer Text Color',
|
|
46
|
+
defaultValue: 'rgba(255, 255, 255, 0.7)'
|
|
47
|
+
}),
|
|
48
|
+
arrowColor: new ColorSetting({
|
|
49
|
+
title: 'Arrow Color',
|
|
50
|
+
defaultValue: 'rgba(255, 255, 255, 0.4)'
|
|
51
|
+
})
|
|
52
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { DividerSettingsMap } from './settings';
|
|
3
|
+
|
|
4
|
+
let { settings }: { settings: DividerSettingsMap } = $props();
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<div class="divider" style="color: {settings.textColor};">
|
|
8
|
+
<div class="divider-line" style="background: {settings.lineColor};"></div>
|
|
9
|
+
<span class="divider-text">{settings.text}</span>
|
|
10
|
+
<div class="divider-line" style="background: {settings.lineColor};"></div>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<style>
|
|
14
|
+
.divider {
|
|
15
|
+
display: flex;
|
|
16
|
+
align-items: center;
|
|
17
|
+
justify-content: center;
|
|
18
|
+
gap: 20px;
|
|
19
|
+
max-width: 750px;
|
|
20
|
+
width: 100%;
|
|
21
|
+
margin: 0 auto;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.divider-line {
|
|
25
|
+
flex: 1;
|
|
26
|
+
height: 1px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.divider-text {
|
|
30
|
+
font-size: 14px;
|
|
31
|
+
font-weight: 800;
|
|
32
|
+
white-space: nowrap;
|
|
33
|
+
}
|
|
34
|
+
</style>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DividerSettingsMap } from './settings';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
settings: DividerSettingsMap;
|
|
4
|
+
};
|
|
5
|
+
declare const DividerElement: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
6
|
+
type DividerElement = ReturnType<typeof DividerElement>;
|
|
7
|
+
export default DividerElement;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ColorSetting, TextSetting, Settings, type InferSettingsMapType } from '../../settings';
|
|
2
|
+
export declare const dividerSettings: Settings<{
|
|
3
|
+
text: TextSetting;
|
|
4
|
+
textColor: ColorSetting;
|
|
5
|
+
lineColor: ColorSetting;
|
|
6
|
+
}>;
|
|
7
|
+
export type DividerSettingsMap = InferSettingsMapType<typeof dividerSettings>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ColorSetting, TextSetting, Settings } from '../../settings';
|
|
2
|
+
export const dividerSettings = new Settings('Divider', {
|
|
3
|
+
text: new TextSetting({
|
|
4
|
+
title: 'Text',
|
|
5
|
+
defaultValue: 'OR'
|
|
6
|
+
}),
|
|
7
|
+
textColor: new ColorSetting({
|
|
8
|
+
title: 'Text Color',
|
|
9
|
+
defaultValue: '#ffffff'
|
|
10
|
+
}),
|
|
11
|
+
lineColor: new ColorSetting({
|
|
12
|
+
title: 'Line Color',
|
|
13
|
+
defaultValue: 'rgba(255,255,255,0.2)'
|
|
14
|
+
})
|
|
15
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ColorSetting, NumberSetting, Settings } from '../settings';
|
|
2
|
+
export declare const globalSettings: Settings<{
|
|
3
|
+
background: ColorSetting;
|
|
4
|
+
paddingTop: NumberSetting;
|
|
5
|
+
paddingBottom: NumberSetting;
|
|
6
|
+
paddingLeft: NumberSetting;
|
|
7
|
+
paddingRight: NumberSetting;
|
|
8
|
+
}>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ColorSetting, NumberSetting, Settings } from '../settings';
|
|
2
|
+
export const globalSettings = new Settings('Global', {
|
|
3
|
+
background: new ColorSetting({
|
|
4
|
+
title: 'Background',
|
|
5
|
+
defaultValue: '#ffffff'
|
|
6
|
+
}),
|
|
7
|
+
paddingTop: new NumberSetting({
|
|
8
|
+
title: 'Padding Top',
|
|
9
|
+
defaultValue: 0,
|
|
10
|
+
extra: {
|
|
11
|
+
min: 0,
|
|
12
|
+
max: 100
|
|
13
|
+
}
|
|
14
|
+
}),
|
|
15
|
+
paddingBottom: new NumberSetting({
|
|
16
|
+
title: 'Padding Bottom',
|
|
17
|
+
defaultValue: 22,
|
|
18
|
+
extra: {
|
|
19
|
+
min: 0,
|
|
20
|
+
max: 100
|
|
21
|
+
}
|
|
22
|
+
}),
|
|
23
|
+
paddingLeft: new NumberSetting({
|
|
24
|
+
title: 'Padding Left',
|
|
25
|
+
defaultValue: 0,
|
|
26
|
+
extra: {
|
|
27
|
+
min: 0,
|
|
28
|
+
max: 100
|
|
29
|
+
}
|
|
30
|
+
}),
|
|
31
|
+
paddingRight: new NumberSetting({
|
|
32
|
+
title: 'Padding Right',
|
|
33
|
+
defaultValue: 0,
|
|
34
|
+
extra: {
|
|
35
|
+
min: 0,
|
|
36
|
+
max: 100
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
});
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ProductsSettingsMap } from './settings';
|
|
3
|
+
|
|
4
|
+
let { settings }: { settings: ProductsSettingsMap } = $props();
|
|
5
|
+
|
|
6
|
+
interface Product {
|
|
7
|
+
_id: string;
|
|
8
|
+
title: string;
|
|
9
|
+
description: string;
|
|
10
|
+
thumbnail: string;
|
|
11
|
+
rating: number;
|
|
12
|
+
stock: number;
|
|
13
|
+
brand: string;
|
|
14
|
+
price: {
|
|
15
|
+
current: number;
|
|
16
|
+
currency: string;
|
|
17
|
+
beforeDiscount: number;
|
|
18
|
+
discountPercentage: number;
|
|
19
|
+
};
|
|
20
|
+
category: {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
image: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let products = $state<Product[]>([]);
|
|
28
|
+
let loading = $state(false);
|
|
29
|
+
let error = $state('');
|
|
30
|
+
|
|
31
|
+
let lastUrl = $state('');
|
|
32
|
+
|
|
33
|
+
$effect(() => {
|
|
34
|
+
const url = settings.apiUrl;
|
|
35
|
+
if (!url || url === lastUrl) return;
|
|
36
|
+
lastUrl = url;
|
|
37
|
+
fetchProducts(url);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
async function fetchProducts(url: string) {
|
|
41
|
+
loading = true;
|
|
42
|
+
error = '';
|
|
43
|
+
try {
|
|
44
|
+
const res = await fetch(url);
|
|
45
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
46
|
+
const data = await res.json();
|
|
47
|
+
products = data.products ?? data.data ?? (Array.isArray(data) ? data : []);
|
|
48
|
+
} catch (e) {
|
|
49
|
+
error = e instanceof Error ? e.message : 'Failed to fetch';
|
|
50
|
+
products = [];
|
|
51
|
+
} finally {
|
|
52
|
+
loading = false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function formatCurrency(amount: number, currency: string) {
|
|
57
|
+
return `${currency === 'USD' ? '$' : currency} ${amount}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function renderStars(rating: number): string {
|
|
61
|
+
const full = Math.floor(rating);
|
|
62
|
+
const half = rating - full >= 0.5 ? 1 : 0;
|
|
63
|
+
const empty = 5 - full - half;
|
|
64
|
+
return '★'.repeat(full) + (half ? '½' : '') + '☆'.repeat(empty);
|
|
65
|
+
}
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<div class="products-wrapper" style="margin-top: {settings.marginTop}px;">
|
|
69
|
+
{#if !settings.apiUrl}
|
|
70
|
+
<div class="placeholder">
|
|
71
|
+
<span class="placeholder-icon">🛒</span>
|
|
72
|
+
<span class="placeholder-text">Enter an API URL in settings to load products</span>
|
|
73
|
+
</div>
|
|
74
|
+
{:else if loading}
|
|
75
|
+
<div class="placeholder">
|
|
76
|
+
<span class="placeholder-text">Loading products...</span>
|
|
77
|
+
</div>
|
|
78
|
+
{:else if error}
|
|
79
|
+
<div class="placeholder error">
|
|
80
|
+
<span class="placeholder-text">Error: {error}</span>
|
|
81
|
+
</div>
|
|
82
|
+
{:else if products.length === 0}
|
|
83
|
+
<div class="placeholder">
|
|
84
|
+
<span class="placeholder-text">No products found</span>
|
|
85
|
+
</div>
|
|
86
|
+
{:else}
|
|
87
|
+
<div class="products-grid" style="grid-template-columns: repeat({settings.columns}, 1fr);">
|
|
88
|
+
{#each products as product (product._id)}
|
|
89
|
+
<div
|
|
90
|
+
class="product-card"
|
|
91
|
+
style="background: {settings.cardBackground}; border: 1px solid {settings.cardBorder};"
|
|
92
|
+
>
|
|
93
|
+
<div class="product-image-wrap">
|
|
94
|
+
<img src={product.thumbnail} alt={product.title} class="product-image" />
|
|
95
|
+
{#if product.price.discountPercentage > 0}
|
|
96
|
+
<span
|
|
97
|
+
class="discount-badge"
|
|
98
|
+
style="background: {settings.discountBadgeBackground}; color: {settings.discountBadgeColor};"
|
|
99
|
+
>
|
|
100
|
+
-{product.price.discountPercentage}%
|
|
101
|
+
</span>
|
|
102
|
+
{/if}
|
|
103
|
+
</div>
|
|
104
|
+
|
|
105
|
+
<div class="product-info">
|
|
106
|
+
<span class="product-brand">{product.brand}</span>
|
|
107
|
+
<h4 class="product-title" style="color: {settings.titleColor};">
|
|
108
|
+
{product.title}
|
|
109
|
+
</h4>
|
|
110
|
+
<p class="product-desc" style="color: {settings.descriptionColor};">
|
|
111
|
+
{product.description.length > 80
|
|
112
|
+
? product.description.slice(0, 80) + '...'
|
|
113
|
+
: product.description}
|
|
114
|
+
</p>
|
|
115
|
+
|
|
116
|
+
<div class="product-rating" style="color: {settings.ratingColor};">
|
|
117
|
+
{renderStars(product.rating)}
|
|
118
|
+
<span class="rating-num">{product.rating.toFixed(1)}</span>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<div class="product-price">
|
|
122
|
+
<span class="current-price" style="color: {settings.priceColor};">
|
|
123
|
+
{formatCurrency(product.price.current, product.price.currency)}
|
|
124
|
+
</span>
|
|
125
|
+
{#if product.price.beforeDiscount > product.price.current}
|
|
126
|
+
<span class="old-price" style="color: {settings.oldPriceColor};">
|
|
127
|
+
{formatCurrency(product.price.beforeDiscount, product.price.currency)}
|
|
128
|
+
</span>
|
|
129
|
+
{/if}
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
{/each}
|
|
134
|
+
</div>
|
|
135
|
+
{/if}
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<style>
|
|
139
|
+
.products-wrapper {
|
|
140
|
+
max-width: 750px;
|
|
141
|
+
width: 100%;
|
|
142
|
+
margin: 0 auto;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.placeholder {
|
|
146
|
+
display: flex;
|
|
147
|
+
flex-direction: column;
|
|
148
|
+
align-items: center;
|
|
149
|
+
justify-content: center;
|
|
150
|
+
gap: 10px;
|
|
151
|
+
padding: 48px 24px;
|
|
152
|
+
border: 1.5px dashed rgba(255, 255, 255, 0.12);
|
|
153
|
+
border-radius: 8px;
|
|
154
|
+
background: rgba(255, 255, 255, 0.03);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.placeholder.error {
|
|
158
|
+
border-color: rgba(239, 68, 68, 0.3);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.placeholder-icon {
|
|
162
|
+
font-size: 2rem;
|
|
163
|
+
opacity: 0.3;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.placeholder-text {
|
|
167
|
+
font-size: 0.8rem;
|
|
168
|
+
color: rgba(255, 255, 255, 0.3);
|
|
169
|
+
text-align: center;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.products-grid {
|
|
173
|
+
display: grid;
|
|
174
|
+
gap: 14px;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.product-card {
|
|
178
|
+
border-radius: 10px;
|
|
179
|
+
overflow: hidden;
|
|
180
|
+
display: flex;
|
|
181
|
+
flex-direction: column;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.product-image-wrap {
|
|
185
|
+
position: relative;
|
|
186
|
+
width: 100%;
|
|
187
|
+
aspect-ratio: 4/3;
|
|
188
|
+
overflow: hidden;
|
|
189
|
+
background: rgba(0, 0, 0, 0.2);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.product-image {
|
|
193
|
+
width: 100%;
|
|
194
|
+
height: 100%;
|
|
195
|
+
object-fit: cover;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.discount-badge {
|
|
199
|
+
position: absolute;
|
|
200
|
+
top: 8px;
|
|
201
|
+
right: 8px;
|
|
202
|
+
padding: 3px 8px;
|
|
203
|
+
border-radius: 6px;
|
|
204
|
+
font-size: 0.7rem;
|
|
205
|
+
font-weight: 700;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.product-info {
|
|
209
|
+
padding: 14px;
|
|
210
|
+
display: flex;
|
|
211
|
+
flex-direction: column;
|
|
212
|
+
gap: 6px;
|
|
213
|
+
flex: 1;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.product-brand {
|
|
217
|
+
font-size: 0.65rem;
|
|
218
|
+
text-transform: uppercase;
|
|
219
|
+
letter-spacing: 0.08em;
|
|
220
|
+
color: rgba(255, 255, 255, 0.35);
|
|
221
|
+
font-weight: 600;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.product-title {
|
|
225
|
+
margin: 0;
|
|
226
|
+
font-size: 0.85rem;
|
|
227
|
+
font-weight: 600;
|
|
228
|
+
line-height: 1.3;
|
|
229
|
+
display: -webkit-box;
|
|
230
|
+
-webkit-line-clamp: 2;
|
|
231
|
+
-webkit-box-orient: vertical;
|
|
232
|
+
overflow: hidden;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.product-desc {
|
|
236
|
+
margin: 0;
|
|
237
|
+
font-size: 0.75rem;
|
|
238
|
+
line-height: 1.4;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.product-rating {
|
|
242
|
+
font-size: 0.75rem;
|
|
243
|
+
display: flex;
|
|
244
|
+
align-items: center;
|
|
245
|
+
gap: 4px;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.rating-num {
|
|
249
|
+
font-size: 0.7rem;
|
|
250
|
+
opacity: 0.6;
|
|
251
|
+
color: rgba(255, 255, 255, 0.5);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.product-price {
|
|
255
|
+
display: flex;
|
|
256
|
+
align-items: baseline;
|
|
257
|
+
gap: 8px;
|
|
258
|
+
margin-top: auto;
|
|
259
|
+
padding-top: 6px;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.current-price {
|
|
263
|
+
font-size: 1rem;
|
|
264
|
+
font-weight: 700;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.old-price {
|
|
268
|
+
font-size: 0.8rem;
|
|
269
|
+
text-decoration: line-through;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
@media (max-width: 640px) {
|
|
273
|
+
.products-grid {
|
|
274
|
+
grid-template-columns: repeat(2, 1fr) !important;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
@media (max-width: 400px) {
|
|
279
|
+
.products-grid {
|
|
280
|
+
grid-template-columns: 1fr !important;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
</style>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ProductsSettingsMap } from './settings';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
settings: ProductsSettingsMap;
|
|
4
|
+
};
|
|
5
|
+
declare const ProductsElement: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
6
|
+
type ProductsElement = ReturnType<typeof ProductsElement>;
|
|
7
|
+
export default ProductsElement;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ColorSetting, NumberSetting, TextSetting, Settings, type InferSettingsMapType } from '../../settings';
|
|
2
|
+
export declare const productsSettings: Settings<{
|
|
3
|
+
marginTop: NumberSetting;
|
|
4
|
+
apiUrl: TextSetting;
|
|
5
|
+
columns: NumberSetting;
|
|
6
|
+
cardBackground: ColorSetting;
|
|
7
|
+
cardBorder: ColorSetting;
|
|
8
|
+
titleColor: ColorSetting;
|
|
9
|
+
descriptionColor: ColorSetting;
|
|
10
|
+
priceColor: ColorSetting;
|
|
11
|
+
oldPriceColor: ColorSetting;
|
|
12
|
+
discountBadgeBackground: ColorSetting;
|
|
13
|
+
discountBadgeColor: ColorSetting;
|
|
14
|
+
ratingColor: ColorSetting;
|
|
15
|
+
}>;
|
|
16
|
+
export type ProductsSettingsMap = InferSettingsMapType<typeof productsSettings>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ColorSetting, NumberSetting, TextSetting, Settings } from '../../settings';
|
|
2
|
+
export const productsSettings = new Settings('Products', {
|
|
3
|
+
marginTop: new NumberSetting({
|
|
4
|
+
title: 'Margin Top',
|
|
5
|
+
defaultValue: 0
|
|
6
|
+
}),
|
|
7
|
+
apiUrl: new TextSetting({
|
|
8
|
+
title: 'API URL',
|
|
9
|
+
defaultValue: ''
|
|
10
|
+
}),
|
|
11
|
+
columns: new NumberSetting({
|
|
12
|
+
title: 'Columns',
|
|
13
|
+
defaultValue: 3,
|
|
14
|
+
extra: {
|
|
15
|
+
min: 1,
|
|
16
|
+
max: 4,
|
|
17
|
+
step: 1
|
|
18
|
+
}
|
|
19
|
+
}),
|
|
20
|
+
cardBackground: new ColorSetting({
|
|
21
|
+
title: 'Card Background',
|
|
22
|
+
defaultValue: '#162D3F'
|
|
23
|
+
}),
|
|
24
|
+
cardBorder: new ColorSetting({
|
|
25
|
+
title: 'Card Border',
|
|
26
|
+
defaultValue: 'rgba(177, 202, 223, 0.1)'
|
|
27
|
+
}),
|
|
28
|
+
titleColor: new ColorSetting({
|
|
29
|
+
title: 'Title Color',
|
|
30
|
+
defaultValue: '#ffffff'
|
|
31
|
+
}),
|
|
32
|
+
descriptionColor: new ColorSetting({
|
|
33
|
+
title: 'Description Color',
|
|
34
|
+
defaultValue: 'rgba(255, 255, 255, 0.6)'
|
|
35
|
+
}),
|
|
36
|
+
priceColor: new ColorSetting({
|
|
37
|
+
title: 'Price Color',
|
|
38
|
+
defaultValue: '#4ade80'
|
|
39
|
+
}),
|
|
40
|
+
oldPriceColor: new ColorSetting({
|
|
41
|
+
title: 'Old Price Color',
|
|
42
|
+
defaultValue: 'rgba(255, 255, 255, 0.35)'
|
|
43
|
+
}),
|
|
44
|
+
discountBadgeBackground: new ColorSetting({
|
|
45
|
+
title: 'Discount Badge Background',
|
|
46
|
+
defaultValue: '#ef4444'
|
|
47
|
+
}),
|
|
48
|
+
discountBadgeColor: new ColorSetting({
|
|
49
|
+
title: 'Discount Badge Text Color',
|
|
50
|
+
defaultValue: '#ffffff'
|
|
51
|
+
}),
|
|
52
|
+
ratingColor: new ColorSetting({
|
|
53
|
+
title: 'Rating Star Color',
|
|
54
|
+
defaultValue: '#f0c845'
|
|
55
|
+
})
|
|
56
|
+
});
|