ds-one 0.2.0-alpha.1 → 0.2.0-alpha.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/DS1/1-root/one.css +1 -1
- package/DS1/2-core/{article-v1.ts → ds-article.ts} +5 -4
- package/DS1/2-core/{attributes-v1.ts → ds-attributes.ts} +4 -3
- package/DS1/2-core/{cycle-v1.ts → ds-cycle.ts} +11 -10
- package/DS1/2-core/{downloadcv-v1.ts → ds-downloadcv.ts} +9 -8
- package/DS1/2-core/{header-v1.ts → ds-header.ts} +4 -3
- package/DS1/2-core/{home-v1.ts → ds-home.ts} +3 -2
- package/DS1/2-core/{icon-v1.ts → ds-icon.ts} +2 -2
- package/DS1/2-core/{link-v1.ts → ds-link.ts} +5 -5
- package/DS1/2-core/{markdown-v1.ts → ds-markdown.ts} +3 -2
- package/DS1/2-core/{price-v1.ts → ds-price.ts} +4 -3
- package/DS1/2-core/{squarecircle-v1.ts → ds-squarecircle.ts} +4 -3
- package/DS1/2-core/{title-v1.ts → ds-title.ts} +5 -4
- package/DS1/2-core/{tooltip-v1.ts → ds-tooltip.ts} +4 -3
- package/DS1/2-core/{viewtoggle-v1.ts → ds-viewtoggle.ts} +4 -3
- package/DS1/2-core/{year-v1.ts → ds-year.ts} +4 -3
- package/DS1/3-unit/{doublenav-v1.ts → ds-doublenav.ts} +9 -8
- package/DS1/3-unit/{list-v1.ts → ds-list.ts} +3 -2
- package/DS1/3-unit/{panel-v1.ts → ds-panel.ts} +3 -2
- package/DS1/3-unit/{row-v1.ts → ds-row.ts} +3 -2
- package/DS1/3-unit/{singlenav-v1.ts → ds-singlenav.ts} +5 -4
- package/DS1/index.ts +20 -20
- package/DS1/utils/cdn-loader.ts +20 -44
- package/README.md +4 -4
- package/dist/2-core/ds-article.d.ts +129 -0
- package/dist/2-core/ds-article.d.ts.map +1 -0
- package/dist/2-core/ds-article.js +361 -0
- package/dist/2-core/ds-attributes.d.ts +47 -0
- package/dist/2-core/ds-attributes.d.ts.map +1 -0
- package/dist/2-core/ds-attributes.js +128 -0
- package/dist/2-core/ds-cycle.d.ts +66 -0
- package/dist/2-core/ds-cycle.d.ts.map +1 -0
- package/dist/2-core/ds-cycle.js +586 -0
- package/dist/2-core/ds-downloadcv.d.ts +58 -0
- package/dist/2-core/ds-downloadcv.d.ts.map +1 -0
- package/dist/2-core/ds-downloadcv.js +119 -0
- package/dist/2-core/ds-header.d.ts +28 -0
- package/dist/2-core/ds-header.d.ts.map +1 -0
- package/dist/2-core/ds-header.js +66 -0
- package/dist/2-core/ds-home.d.ts +26 -0
- package/dist/2-core/ds-home.d.ts.map +1 -0
- package/dist/2-core/ds-home.js +148 -0
- package/dist/2-core/ds-icon.d.ts +28 -0
- package/dist/2-core/ds-icon.d.ts.map +1 -0
- package/dist/2-core/ds-icon.js +297 -0
- package/dist/2-core/ds-link.d.ts +35 -0
- package/dist/2-core/ds-link.d.ts.map +1 -0
- package/dist/2-core/ds-link.js +85 -0
- package/dist/2-core/ds-markdown.d.ts +7 -0
- package/dist/2-core/ds-markdown.d.ts.map +1 -0
- package/dist/2-core/ds-markdown.js +240 -0
- package/dist/2-core/ds-price.d.ts +46 -0
- package/dist/2-core/ds-price.d.ts.map +1 -0
- package/dist/2-core/ds-price.js +72 -0
- package/dist/2-core/ds-squarecircle.d.ts +50 -0
- package/dist/2-core/ds-squarecircle.d.ts.map +1 -0
- package/dist/2-core/ds-squarecircle.js +133 -0
- package/dist/2-core/ds-title.d.ts +50 -0
- package/dist/2-core/ds-title.d.ts.map +1 -0
- package/dist/2-core/ds-title.js +103 -0
- package/dist/2-core/ds-tooltip.d.ts +39 -0
- package/dist/2-core/ds-tooltip.d.ts.map +1 -0
- package/dist/2-core/ds-tooltip.js +145 -0
- package/dist/2-core/ds-viewtoggle.d.ts +27 -0
- package/dist/2-core/ds-viewtoggle.d.ts.map +1 -0
- package/dist/2-core/ds-viewtoggle.js +49 -0
- package/dist/2-core/ds-year.d.ts +16 -0
- package/dist/2-core/ds-year.d.ts.map +1 -0
- package/dist/2-core/ds-year.js +21 -0
- package/dist/3-unit/ds-doublenav.d.ts +51 -0
- package/dist/3-unit/ds-doublenav.d.ts.map +1 -0
- package/dist/3-unit/ds-doublenav.js +88 -0
- package/dist/3-unit/ds-list.d.ts +11 -0
- package/dist/3-unit/ds-list.d.ts.map +1 -0
- package/dist/3-unit/ds-list.js +15 -0
- package/dist/3-unit/ds-panel.d.ts +11 -0
- package/dist/3-unit/ds-panel.d.ts.map +1 -0
- package/dist/3-unit/ds-panel.js +16 -0
- package/dist/3-unit/ds-row.d.ts +25 -0
- package/dist/3-unit/ds-row.d.ts.map +1 -0
- package/dist/3-unit/ds-row.js +32 -0
- package/dist/3-unit/ds-singlenav.d.ts +32 -0
- package/dist/3-unit/ds-singlenav.d.ts.map +1 -0
- package/dist/3-unit/ds-singlenav.js +62 -0
- package/dist/ds-one.bundle.js +79 -99
- package/dist/ds-one.bundle.js.map +3 -3
- package/dist/ds-one.bundle.min.js +47 -47
- package/dist/ds-one.bundle.min.js.map +4 -4
- package/dist/index.d.ts +20 -20
- package/dist/index.js +20 -20
- package/dist/utils/cdn-loader.d.ts.map +1 -1
- package/dist/utils/cdn-loader.js +20 -38
- package/package.json +2 -2
package/DS1/utils/cdn-loader.ts
CHANGED
|
@@ -17,20 +17,7 @@ declare global {
|
|
|
17
17
|
// Track if we've already attempted to load
|
|
18
18
|
let loadAttempted = false;
|
|
19
19
|
|
|
20
|
-
const
|
|
21
|
-
"./keys.json",
|
|
22
|
-
"./tekst.json",
|
|
23
|
-
"./tekster.json",
|
|
24
|
-
"./language.json",
|
|
25
|
-
"./languages.json",
|
|
26
|
-
"./translations.json",
|
|
27
|
-
"./translate.json",
|
|
28
|
-
"./i18n.json",
|
|
29
|
-
"./locales.json",
|
|
30
|
-
"./strings.json",
|
|
31
|
-
"./text.json",
|
|
32
|
-
"./texts.json",
|
|
33
|
-
];
|
|
20
|
+
const DEFAULT_TRANSLATION_FILE = "./translations.json";
|
|
34
21
|
|
|
35
22
|
function normalizeCandidate(path: string): string | null {
|
|
36
23
|
if (!path) {
|
|
@@ -86,26 +73,29 @@ function findAttributeCandidate(): string | null {
|
|
|
86
73
|
}
|
|
87
74
|
|
|
88
75
|
function resolveTranslationSources(): string[] {
|
|
89
|
-
const candidates =
|
|
76
|
+
const candidates: string[] = [];
|
|
90
77
|
|
|
91
78
|
const windowCandidate =
|
|
92
79
|
typeof window !== "undefined" ? window.DS_ONE_TRANSLATIONS_FILE : null;
|
|
93
80
|
const attributeCandidate = findAttributeCandidate();
|
|
94
81
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
};
|
|
82
|
+
// Only use explicitly configured paths, or the single default
|
|
83
|
+
const windowNormalized = normalizeCandidate(windowCandidate ?? "");
|
|
84
|
+
if (windowNormalized) {
|
|
85
|
+
candidates.push(windowNormalized);
|
|
86
|
+
}
|
|
101
87
|
|
|
102
|
-
|
|
103
|
-
|
|
88
|
+
const attrNormalized = normalizeCandidate(attributeCandidate ?? "");
|
|
89
|
+
if (attrNormalized && !candidates.includes(attrNormalized)) {
|
|
90
|
+
candidates.push(attrNormalized);
|
|
91
|
+
}
|
|
104
92
|
|
|
105
|
-
//
|
|
106
|
-
|
|
93
|
+
// Only try default if no explicit path was configured
|
|
94
|
+
if (candidates.length === 0) {
|
|
95
|
+
candidates.push(DEFAULT_TRANSLATION_FILE);
|
|
96
|
+
}
|
|
107
97
|
|
|
108
|
-
return
|
|
98
|
+
return candidates;
|
|
109
99
|
}
|
|
110
100
|
|
|
111
101
|
function validateTranslationMap(
|
|
@@ -124,16 +114,14 @@ async function fetchTranslationFile(
|
|
|
124
114
|
source: string
|
|
125
115
|
): Promise<TranslationMap | null> {
|
|
126
116
|
try {
|
|
127
|
-
console.log(`[DS one] Attempting to fetch translations from: ${source}`);
|
|
128
117
|
const response = await fetch(source);
|
|
129
118
|
|
|
130
119
|
if (!response.ok) {
|
|
131
|
-
|
|
120
|
+
// 404 is expected if no translations file exists - don't log as error
|
|
132
121
|
return null;
|
|
133
122
|
}
|
|
134
123
|
|
|
135
124
|
const translations = await response.json();
|
|
136
|
-
console.log(`[DS one] Successfully fetched JSON from ${source}`);
|
|
137
125
|
|
|
138
126
|
if (!validateTranslationMap(translations)) {
|
|
139
127
|
console.warn(
|
|
@@ -148,21 +136,9 @@ async function fetchTranslationFile(
|
|
|
148
136
|
return null;
|
|
149
137
|
}
|
|
150
138
|
|
|
151
|
-
console.log(`[DS one] Valid translations found: ${languages.join(", ")}`);
|
|
152
139
|
return translations;
|
|
153
|
-
} catch
|
|
154
|
-
|
|
155
|
-
error instanceof TypeError &&
|
|
156
|
-
error.message.includes("Failed to fetch")
|
|
157
|
-
) {
|
|
158
|
-
console.log(`[DS one] Network error fetching ${source}`);
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
console.error(
|
|
163
|
-
`[DS one] Error loading external translations from ${source}:`,
|
|
164
|
-
error
|
|
165
|
-
);
|
|
140
|
+
} catch {
|
|
141
|
+
// Silently fail - file likely doesn't exist or isn't valid JSON
|
|
166
142
|
return null;
|
|
167
143
|
}
|
|
168
144
|
}
|
|
@@ -212,7 +188,7 @@ export async function loadExternalTranslations(): Promise<boolean> {
|
|
|
212
188
|
}
|
|
213
189
|
|
|
214
190
|
console.info(
|
|
215
|
-
`[DS one] No external translations found
|
|
191
|
+
`[DS one] No external translations found at ${sources[0] ?? DEFAULT_TRANSLATION_FILE}. Using bundled translations.`
|
|
216
192
|
);
|
|
217
193
|
|
|
218
194
|
return false;
|
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ npm install ds-one@alpha
|
|
|
26
26
|
yarn add ds-one@alpha
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
**Note**: Currently published as alpha version `0.2.0-alpha.
|
|
29
|
+
**Note**: Currently published as alpha version `0.2.0-alpha.3`. Use `@alpha` tag to install.
|
|
30
30
|
|
|
31
31
|
### Basic Usage (CDN)
|
|
32
32
|
|
|
@@ -86,13 +86,13 @@ Try DS one in your browser: **[dsone.dev](https://dsone.dev)** (documentation sl
|
|
|
86
86
|
- **[Internationalization](./docs/i18n.md)** - Language keys and Notion CMS setup
|
|
87
87
|
- **[Examples](./docs/examples.md)** - Usage examples and patterns
|
|
88
88
|
|
|
89
|
-
## Current Status: v0.2.0-alpha.
|
|
89
|
+
## Current Status: v0.2.0-alpha.3
|
|
90
90
|
|
|
91
91
|
**⚠️ Alpha Release**: This is an early alpha version. The API may change as we refine the components and architecture.
|
|
92
92
|
|
|
93
93
|
### Completed Features
|
|
94
94
|
|
|
95
|
-
- ✅ Core component library (ds-button, ds-text, icon
|
|
95
|
+
- ✅ Core component library (ds-button, ds-text, ds-icon, etc.)
|
|
96
96
|
- ✅ Theming system with accent color support
|
|
97
97
|
- ✅ Internationalization with language keys
|
|
98
98
|
- ✅ Responsive design with mobile scaling
|
|
@@ -183,7 +183,7 @@ bun run build
|
|
|
183
183
|
|
|
184
184
|
```bash
|
|
185
185
|
# Create a new alpha release (recommended for now)
|
|
186
|
-
bun run release:pre:alpha # Bumps alpha version (e.g., 0.2.0-alpha.
|
|
186
|
+
bun run release:pre:alpha # Bumps alpha version (e.g., 0.2.0-alpha.3 → 0.2.0-alpha.3)
|
|
187
187
|
|
|
188
188
|
# Other release commands (for future use)
|
|
189
189
|
bun run release:patch # For patch releases
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
/**
|
|
3
|
+
* A component for displaying article content with image/text toggle
|
|
4
|
+
*
|
|
5
|
+
* @element ds-article
|
|
6
|
+
*/
|
|
7
|
+
export declare class Article extends LitElement {
|
|
8
|
+
static get properties(): {
|
|
9
|
+
imgLightDesktop: {
|
|
10
|
+
type: StringConstructor;
|
|
11
|
+
reflect: boolean;
|
|
12
|
+
attribute: string;
|
|
13
|
+
};
|
|
14
|
+
imgDarkDesktop: {
|
|
15
|
+
type: StringConstructor;
|
|
16
|
+
reflect: boolean;
|
|
17
|
+
attribute: string;
|
|
18
|
+
};
|
|
19
|
+
imgLightMobile: {
|
|
20
|
+
type: StringConstructor;
|
|
21
|
+
reflect: boolean;
|
|
22
|
+
attribute: string;
|
|
23
|
+
};
|
|
24
|
+
imgDarkMobile: {
|
|
25
|
+
type: StringConstructor;
|
|
26
|
+
reflect: boolean;
|
|
27
|
+
attribute: string;
|
|
28
|
+
};
|
|
29
|
+
src: {
|
|
30
|
+
type: StringConstructor;
|
|
31
|
+
reflect: boolean;
|
|
32
|
+
};
|
|
33
|
+
alt: {
|
|
34
|
+
type: StringConstructor;
|
|
35
|
+
reflect: boolean;
|
|
36
|
+
};
|
|
37
|
+
imageToggle: {
|
|
38
|
+
type: BooleanConstructor;
|
|
39
|
+
reflect: boolean;
|
|
40
|
+
attribute: string;
|
|
41
|
+
};
|
|
42
|
+
imageOnlyWidth: {
|
|
43
|
+
type: NumberConstructor;
|
|
44
|
+
reflect: boolean;
|
|
45
|
+
attribute: string;
|
|
46
|
+
};
|
|
47
|
+
priority: {
|
|
48
|
+
type: BooleanConstructor;
|
|
49
|
+
reflect: boolean;
|
|
50
|
+
};
|
|
51
|
+
externalToggle: {
|
|
52
|
+
type: BooleanConstructor;
|
|
53
|
+
reflect: boolean;
|
|
54
|
+
attribute: string;
|
|
55
|
+
};
|
|
56
|
+
mobileKey: {
|
|
57
|
+
type: StringConstructor;
|
|
58
|
+
reflect: boolean;
|
|
59
|
+
attribute: string;
|
|
60
|
+
};
|
|
61
|
+
desktopKey: {
|
|
62
|
+
type: StringConstructor;
|
|
63
|
+
reflect: boolean;
|
|
64
|
+
attribute: string;
|
|
65
|
+
};
|
|
66
|
+
textKey: {
|
|
67
|
+
type: StringConstructor;
|
|
68
|
+
reflect: boolean;
|
|
69
|
+
attribute: string;
|
|
70
|
+
};
|
|
71
|
+
defaultValue: {
|
|
72
|
+
type: StringConstructor;
|
|
73
|
+
reflect: boolean;
|
|
74
|
+
attribute: string;
|
|
75
|
+
};
|
|
76
|
+
_text: {
|
|
77
|
+
type: StringConstructor;
|
|
78
|
+
state: boolean;
|
|
79
|
+
};
|
|
80
|
+
_currentImage: {
|
|
81
|
+
type: StringConstructor;
|
|
82
|
+
state: boolean;
|
|
83
|
+
};
|
|
84
|
+
_imageVisible: {
|
|
85
|
+
type: BooleanConstructor;
|
|
86
|
+
state: boolean;
|
|
87
|
+
};
|
|
88
|
+
_shouldAnimate: {
|
|
89
|
+
type: BooleanConstructor;
|
|
90
|
+
state: boolean;
|
|
91
|
+
};
|
|
92
|
+
};
|
|
93
|
+
imgLightDesktop: string;
|
|
94
|
+
imgDarkDesktop: string;
|
|
95
|
+
imgLightMobile: string;
|
|
96
|
+
imgDarkMobile: string;
|
|
97
|
+
src: string;
|
|
98
|
+
alt: string;
|
|
99
|
+
imageToggle: boolean;
|
|
100
|
+
imageOnlyWidth: number;
|
|
101
|
+
priority: boolean;
|
|
102
|
+
externalToggle: boolean;
|
|
103
|
+
mobileKey: string;
|
|
104
|
+
desktopKey: string;
|
|
105
|
+
textKey: string;
|
|
106
|
+
defaultValue: string;
|
|
107
|
+
_text: string;
|
|
108
|
+
_currentImage: string;
|
|
109
|
+
_imageVisible: boolean;
|
|
110
|
+
_shouldAnimate: boolean;
|
|
111
|
+
private _mql;
|
|
112
|
+
private boundHandlers;
|
|
113
|
+
constructor();
|
|
114
|
+
static styles: import("lit").CSSResult;
|
|
115
|
+
updated(changed: Map<string, unknown>): void;
|
|
116
|
+
connectedCallback(): void;
|
|
117
|
+
disconnectedCallback(): void;
|
|
118
|
+
private _loadText;
|
|
119
|
+
private _resolveImage;
|
|
120
|
+
private _toggleImage;
|
|
121
|
+
private _handleImageLoad;
|
|
122
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
123
|
+
}
|
|
124
|
+
declare global {
|
|
125
|
+
interface HTMLElementTagNameMap {
|
|
126
|
+
"ds-article": Article;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=ds-article.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ds-article.d.ts","sourceRoot":"","sources":["../../DS1/2-core/ds-article.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAI5C;;;;GAIG;AACH,qBAAa,OAAQ,SAAQ,UAAU;IACrC,MAAM,KAAK,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;MA6CpB;IAEO,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IAEhC,OAAO,CAAC,IAAI,CAAwB;IACpC,OAAO,CAAC,aAAa,CAKnB;;IAiDF,MAAM,CAAC,MAAM,0BAmEX;IAEF,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAkCrC,iBAAiB;IAuDjB,oBAAoB;IA+BpB,OAAO,CAAC,SAAS;IAsCjB,OAAO,CAAC,aAAa;IAwCrB,OAAO,CAAC,YAAY,CAIlB;IAEF,OAAO,CAAC,gBAAgB,CAGtB;IAEF,MAAM;CAmCP;AAID,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,YAAY,EAAE,OAAO,CAAC;KACvB;CACF"}
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { LitElement, css, html } from "lit";
|
|
2
|
+
import { getText } from "../utils/language";
|
|
3
|
+
import { getViewMode } from "../utils/viewMode";
|
|
4
|
+
/**
|
|
5
|
+
* A component for displaying article content with image/text toggle
|
|
6
|
+
*
|
|
7
|
+
* @element ds-article
|
|
8
|
+
*/
|
|
9
|
+
export class Article extends LitElement {
|
|
10
|
+
static get properties() {
|
|
11
|
+
return {
|
|
12
|
+
imgLightDesktop: {
|
|
13
|
+
type: String,
|
|
14
|
+
reflect: true,
|
|
15
|
+
attribute: "img-light-desktop",
|
|
16
|
+
},
|
|
17
|
+
imgDarkDesktop: {
|
|
18
|
+
type: String,
|
|
19
|
+
reflect: true,
|
|
20
|
+
attribute: "img-dark-desktop",
|
|
21
|
+
},
|
|
22
|
+
imgLightMobile: {
|
|
23
|
+
type: String,
|
|
24
|
+
reflect: true,
|
|
25
|
+
attribute: "img-light-mobile",
|
|
26
|
+
},
|
|
27
|
+
imgDarkMobile: {
|
|
28
|
+
type: String,
|
|
29
|
+
reflect: true,
|
|
30
|
+
attribute: "img-dark-mobile",
|
|
31
|
+
},
|
|
32
|
+
src: { type: String, reflect: true },
|
|
33
|
+
alt: { type: String, reflect: true },
|
|
34
|
+
imageToggle: { type: Boolean, reflect: true, attribute: "image-toggle" },
|
|
35
|
+
imageOnlyWidth: {
|
|
36
|
+
type: Number,
|
|
37
|
+
reflect: true,
|
|
38
|
+
attribute: "image-only-width",
|
|
39
|
+
},
|
|
40
|
+
priority: { type: Boolean, reflect: true },
|
|
41
|
+
externalToggle: {
|
|
42
|
+
type: Boolean,
|
|
43
|
+
reflect: true,
|
|
44
|
+
attribute: "external-toggle",
|
|
45
|
+
},
|
|
46
|
+
mobileKey: { type: String, reflect: true, attribute: "mobile-key" },
|
|
47
|
+
desktopKey: { type: String, reflect: true, attribute: "desktop-key" },
|
|
48
|
+
textKey: { type: String, reflect: true, attribute: "text-key" },
|
|
49
|
+
defaultValue: { type: String, reflect: true, attribute: "default-value" },
|
|
50
|
+
_text: { type: String, state: true },
|
|
51
|
+
_currentImage: { type: String, state: true },
|
|
52
|
+
_imageVisible: { type: Boolean, state: true },
|
|
53
|
+
_shouldAnimate: { type: Boolean, state: true },
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
constructor() {
|
|
57
|
+
super();
|
|
58
|
+
this._toggleImage = () => {
|
|
59
|
+
this._shouldAnimate = true;
|
|
60
|
+
this._imageVisible = !this._imageVisible;
|
|
61
|
+
this.requestUpdate();
|
|
62
|
+
};
|
|
63
|
+
this._handleImageLoad = (e) => {
|
|
64
|
+
const img = e.target;
|
|
65
|
+
img.classList.add("loaded");
|
|
66
|
+
};
|
|
67
|
+
this.imgLightDesktop = "";
|
|
68
|
+
this.imgDarkDesktop = "";
|
|
69
|
+
this.imgLightMobile = "";
|
|
70
|
+
this.imgDarkMobile = "";
|
|
71
|
+
this.src = "";
|
|
72
|
+
this.alt = "";
|
|
73
|
+
this.imageToggle = false;
|
|
74
|
+
this.imageOnlyWidth = 480;
|
|
75
|
+
this.priority = false;
|
|
76
|
+
this.externalToggle = false;
|
|
77
|
+
this.mobileKey = "";
|
|
78
|
+
this.desktopKey = "";
|
|
79
|
+
this.textKey = "";
|
|
80
|
+
this.defaultValue = "";
|
|
81
|
+
this._text = "";
|
|
82
|
+
this._currentImage = "";
|
|
83
|
+
this._imageVisible = true;
|
|
84
|
+
this._shouldAnimate = false;
|
|
85
|
+
this._mql = null;
|
|
86
|
+
this.boundHandlers = {
|
|
87
|
+
languageChanged: (() => {
|
|
88
|
+
this._loadText();
|
|
89
|
+
}),
|
|
90
|
+
themeChanged: (() => {
|
|
91
|
+
this._resolveImage();
|
|
92
|
+
}),
|
|
93
|
+
viewportChanged: (e) => {
|
|
94
|
+
this._resolveImage();
|
|
95
|
+
this._loadText();
|
|
96
|
+
},
|
|
97
|
+
viewModeChanged: ((e) => {
|
|
98
|
+
if (this.imageToggle && this.externalToggle) {
|
|
99
|
+
const newMode = e.detail;
|
|
100
|
+
const shouldShowImage = newMode === "image";
|
|
101
|
+
if (this._imageVisible !== shouldShowImage) {
|
|
102
|
+
this._shouldAnimate = true;
|
|
103
|
+
this._imageVisible = shouldShowImage;
|
|
104
|
+
this.requestUpdate();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
updated(changed) {
|
|
111
|
+
if (changed.has("imageOnlyWidth")) {
|
|
112
|
+
const opx = Number(this.imageOnlyWidth) || 480;
|
|
113
|
+
this.style.setProperty("--image-only-width", `${opx}px`);
|
|
114
|
+
}
|
|
115
|
+
if (changed.has("imgLightDesktop") ||
|
|
116
|
+
changed.has("imgDarkDesktop") ||
|
|
117
|
+
changed.has("imgLightMobile") ||
|
|
118
|
+
changed.has("imgDarkMobile") ||
|
|
119
|
+
changed.has("src")) {
|
|
120
|
+
this._resolveImage();
|
|
121
|
+
}
|
|
122
|
+
if (changed.has("textKey") ||
|
|
123
|
+
changed.has("defaultValue") ||
|
|
124
|
+
changed.has("mobileKey") ||
|
|
125
|
+
changed.has("desktopKey")) {
|
|
126
|
+
this._loadText();
|
|
127
|
+
}
|
|
128
|
+
if (changed.has("imageToggle")) {
|
|
129
|
+
const isExternallyControlled = this.imageToggle && this.externalToggle;
|
|
130
|
+
if (!isExternallyControlled) {
|
|
131
|
+
const newImageVisible = !this.imageToggle;
|
|
132
|
+
if (this._imageVisible !== newImageVisible) {
|
|
133
|
+
this._shouldAnimate = true;
|
|
134
|
+
this._imageVisible = newImageVisible;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
connectedCallback() {
|
|
140
|
+
super.connectedCallback();
|
|
141
|
+
this._loadText();
|
|
142
|
+
this._resolveImage();
|
|
143
|
+
if (this.imageToggle && this.externalToggle) {
|
|
144
|
+
const currentMode = getViewMode();
|
|
145
|
+
this._imageVisible = currentMode === "image";
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this._imageVisible = !this.imageToggle;
|
|
149
|
+
}
|
|
150
|
+
this.style.setProperty("--image-only-width", `${this.imageOnlyWidth}px`);
|
|
151
|
+
try {
|
|
152
|
+
this._mql = window.matchMedia("(max-width: 720px)");
|
|
153
|
+
if (this._mql && this.boundHandlers.viewportChanged) {
|
|
154
|
+
this._mql.addEventListener("change", this.boundHandlers.viewportChanged);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
// no-op
|
|
159
|
+
}
|
|
160
|
+
window.addEventListener("language-changed", this.boundHandlers.languageChanged);
|
|
161
|
+
window.addEventListener("theme-changed", this.boundHandlers.themeChanged);
|
|
162
|
+
if (this.imageToggle && this.externalToggle) {
|
|
163
|
+
window.addEventListener("view-mode-changed", this.boundHandlers.viewModeChanged);
|
|
164
|
+
setTimeout(() => {
|
|
165
|
+
const currentMode = getViewMode();
|
|
166
|
+
const shouldShowImage = currentMode === "image";
|
|
167
|
+
if (this._imageVisible !== shouldShowImage) {
|
|
168
|
+
this._shouldAnimate = true;
|
|
169
|
+
this._imageVisible = shouldShowImage;
|
|
170
|
+
this.requestUpdate();
|
|
171
|
+
}
|
|
172
|
+
}, 100);
|
|
173
|
+
}
|
|
174
|
+
else if (this.imageToggle) {
|
|
175
|
+
window.addEventListener("article-toggle", this._toggleImage);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
disconnectedCallback() {
|
|
179
|
+
super.disconnectedCallback();
|
|
180
|
+
window.removeEventListener("language-changed", this.boundHandlers.languageChanged);
|
|
181
|
+
window.removeEventListener("theme-changed", this.boundHandlers.themeChanged);
|
|
182
|
+
window.removeEventListener("article-toggle", this._toggleImage);
|
|
183
|
+
window.removeEventListener("view-mode-changed", this.boundHandlers.viewModeChanged);
|
|
184
|
+
if (this._mql && this.boundHandlers.viewportChanged) {
|
|
185
|
+
try {
|
|
186
|
+
this._mql.removeEventListener("change", this.boundHandlers.viewportChanged);
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
// no-op
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
_loadText() {
|
|
194
|
+
const isMobile = (() => {
|
|
195
|
+
try {
|
|
196
|
+
return (window.matchMedia && window.matchMedia("(max-width: 720px)").matches);
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
})();
|
|
202
|
+
const selectedKey = (() => {
|
|
203
|
+
const mobile = (this.mobileKey || "").trim();
|
|
204
|
+
const desktop = (this.desktopKey || "").trim();
|
|
205
|
+
const base = (this.textKey || "").trim();
|
|
206
|
+
if (mobile || desktop) {
|
|
207
|
+
return isMobile ? mobile || base : desktop || base;
|
|
208
|
+
}
|
|
209
|
+
return base;
|
|
210
|
+
})();
|
|
211
|
+
if (!selectedKey) {
|
|
212
|
+
this._text = this.defaultValue || "";
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
const text = getText(selectedKey);
|
|
217
|
+
this._text = text || this.defaultValue || selectedKey;
|
|
218
|
+
}
|
|
219
|
+
catch (error) {
|
|
220
|
+
console.error(`[ds-article] Error loading text for key "${selectedKey}":`, error);
|
|
221
|
+
this._text = this.defaultValue || selectedKey;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
_resolveImage() {
|
|
225
|
+
if (this.src && typeof this.src === "string") {
|
|
226
|
+
const direct = this.src.startsWith("@") ? this.src.slice(1) : this.src;
|
|
227
|
+
this._currentImage = direct;
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
const theme = document.documentElement.getAttribute("data-theme") || "light";
|
|
231
|
+
const isMobile = (() => {
|
|
232
|
+
try {
|
|
233
|
+
return (window.matchMedia && window.matchMedia("(max-width: 720px)").matches);
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
})();
|
|
239
|
+
let chosen = "";
|
|
240
|
+
if (isMobile) {
|
|
241
|
+
if (theme === "dark") {
|
|
242
|
+
chosen = this.imgDarkMobile || this.imgLightMobile;
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
chosen = this.imgLightMobile || this.imgDarkMobile;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
if (theme === "dark") {
|
|
250
|
+
chosen = this.imgDarkDesktop || this.imgLightDesktop;
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
chosen = this.imgLightDesktop || this.imgDarkDesktop;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (typeof chosen === "string" && chosen.startsWith("@")) {
|
|
257
|
+
chosen = chosen.slice(1);
|
|
258
|
+
}
|
|
259
|
+
this._currentImage = chosen || "";
|
|
260
|
+
}
|
|
261
|
+
render() {
|
|
262
|
+
const showImage = (this.imageToggle ? this._imageVisible : true) && !!this._currentImage;
|
|
263
|
+
const animationClass = this._shouldAnimate ? "fade-in" : "";
|
|
264
|
+
if (this._shouldAnimate) {
|
|
265
|
+
this._shouldAnimate = false;
|
|
266
|
+
}
|
|
267
|
+
const buttonText = this._imageVisible
|
|
268
|
+
? getText("hideImage")
|
|
269
|
+
: getText("viewImage");
|
|
270
|
+
return html `
|
|
271
|
+
<div class="container">
|
|
272
|
+
${this.imageToggle && !this.externalToggle
|
|
273
|
+
? html `<button @click="${this._toggleImage}" class="toggle-button">
|
|
274
|
+
${buttonText}
|
|
275
|
+
</button>`
|
|
276
|
+
: ""}
|
|
277
|
+
${showImage
|
|
278
|
+
? html `<figure class="media ${animationClass}">
|
|
279
|
+
<img
|
|
280
|
+
src="${this._currentImage}"
|
|
281
|
+
alt="${this.alt || ""}"
|
|
282
|
+
loading="${this.priority ? "eager" : "lazy"}"
|
|
283
|
+
fetchpriority="${this.priority ? "high" : "auto"}"
|
|
284
|
+
decoding="async"
|
|
285
|
+
@load="${this._handleImageLoad}"
|
|
286
|
+
/>
|
|
287
|
+
</figure>`
|
|
288
|
+
: html `<p class="copy ${animationClass}">${this._text}</p>`}
|
|
289
|
+
</div>
|
|
290
|
+
`;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
Article.styles = css `
|
|
294
|
+
:host {
|
|
295
|
+
display: block;
|
|
296
|
+
font-family: var(--typeface);
|
|
297
|
+
overflow-wrap: break-word;
|
|
298
|
+
word-wrap: break-word;
|
|
299
|
+
word-break: break-word;
|
|
300
|
+
hyphens: auto;
|
|
301
|
+
color: light-dark(var(--black), var(--white));
|
|
302
|
+
line-height: calc(20px * var(--scaling-factor));
|
|
303
|
+
padding: calc(2px * var(--scaling-factor));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.container {
|
|
307
|
+
position: relative;
|
|
308
|
+
width: min(100%, var(--image-only-width));
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
figure.media,
|
|
312
|
+
.copy {
|
|
313
|
+
margin: 0;
|
|
314
|
+
width: 100%;
|
|
315
|
+
overflow: hidden;
|
|
316
|
+
display: block;
|
|
317
|
+
margin-bottom: calc(8px * var(--scaling-factor));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.fade-in {
|
|
321
|
+
animation: fade-in 0.25s ease;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
@keyframes fade-in {
|
|
325
|
+
from {
|
|
326
|
+
opacity: 0;
|
|
327
|
+
}
|
|
328
|
+
to {
|
|
329
|
+
opacity: 1;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
img {
|
|
334
|
+
display: block;
|
|
335
|
+
width: 100%;
|
|
336
|
+
height: auto;
|
|
337
|
+
opacity: 0;
|
|
338
|
+
transition: opacity 0.2s ease-in-out;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
img.loaded {
|
|
342
|
+
opacity: 1;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.toggle-button {
|
|
346
|
+
background: none;
|
|
347
|
+
border: none;
|
|
348
|
+
color: inherit;
|
|
349
|
+
cursor: pointer;
|
|
350
|
+
font-family: var(--typeface);
|
|
351
|
+
font-size: calc(14px * var(--scaling-factor));
|
|
352
|
+
margin-bottom: calc(8px * var(--scaling-factor));
|
|
353
|
+
padding: 0;
|
|
354
|
+
text-decoration: underline;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
.toggle-button:hover {
|
|
358
|
+
opacity: 0.7;
|
|
359
|
+
}
|
|
360
|
+
`;
|
|
361
|
+
customElements.define("ds-article", Article);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { LitElement } from "lit";
|
|
2
|
+
/**
|
|
3
|
+
* A component for displaying project/work attributes (year, category, status)
|
|
4
|
+
*
|
|
5
|
+
* @element ds-attributes
|
|
6
|
+
* @prop {string} year - Year key for translation
|
|
7
|
+
* @prop {string} category - Category key for translation
|
|
8
|
+
* @prop {string} status - Status key for translation
|
|
9
|
+
* @prop {string} type - Type of attributes: "project" or "work"
|
|
10
|
+
*/
|
|
11
|
+
export declare class Attributes extends LitElement {
|
|
12
|
+
static get properties(): {
|
|
13
|
+
year: {
|
|
14
|
+
type: StringConstructor;
|
|
15
|
+
reflect: boolean;
|
|
16
|
+
};
|
|
17
|
+
category: {
|
|
18
|
+
type: StringConstructor;
|
|
19
|
+
reflect: boolean;
|
|
20
|
+
};
|
|
21
|
+
status: {
|
|
22
|
+
type: StringConstructor;
|
|
23
|
+
reflect: boolean;
|
|
24
|
+
};
|
|
25
|
+
type: {
|
|
26
|
+
type: StringConstructor;
|
|
27
|
+
reflect: boolean;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
year: string;
|
|
31
|
+
category: string;
|
|
32
|
+
status: string;
|
|
33
|
+
type: string;
|
|
34
|
+
private boundHandlers;
|
|
35
|
+
static styles: import("lit").CSSResult;
|
|
36
|
+
constructor();
|
|
37
|
+
connectedCallback(): void;
|
|
38
|
+
disconnectedCallback(): void;
|
|
39
|
+
_getStatusClass(status: string): string;
|
|
40
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
41
|
+
}
|
|
42
|
+
declare global {
|
|
43
|
+
interface HTMLElementTagNameMap {
|
|
44
|
+
"ds-attributes": Attributes;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=ds-attributes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ds-attributes.d.ts","sourceRoot":"","sources":["../../DS1/2-core/ds-attributes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAG5C;;;;;;;;GAQG;AACH,qBAAa,UAAW,SAAQ,UAAU;IACxC,MAAM,KAAK,UAAU;;;;;;;;;;;;;;;;;MAOpB;IAEO,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IAErB,OAAO,CAAC,aAAa,CAAqC;IAE1D,MAAM,CAAC,MAAM,0BA2CX;;IAgBF,iBAAiB;IAQjB,oBAAoB;IAQpB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAQvC,MAAM;CAiCP;AAID,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,eAAe,EAAE,UAAU,CAAC;KAC7B;CACF"}
|