@screenly/edge-apps 0.0.1 → 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/dist/assets/fonts/Inter-Medium.woff2 +0 -0
- package/dist/assets/fonts/Inter-Regular.woff2 +0 -0
- package/dist/assets/fonts/Inter-SemiBold.woff2 +0 -0
- package/dist/assets/images/icons/chancesleet.svg +4 -0
- package/dist/assets/images/icons/clear-night.svg +5 -0
- package/dist/assets/images/icons/clear.svg +11 -0
- package/dist/assets/images/icons/cloudy.svg +4 -0
- package/dist/assets/images/icons/drizzle.svg +5 -0
- package/dist/assets/images/icons/fewdrops.svg +4 -0
- package/dist/assets/images/icons/fog.svg +6 -0
- package/dist/assets/images/icons/haze.svg +6 -0
- package/dist/assets/images/icons/mostly-cloudy-night.svg +7 -0
- package/dist/assets/images/icons/mostly-cloudy.svg +13 -0
- package/dist/assets/images/icons/partially-cloudy-night.svg +6 -0
- package/dist/assets/images/icons/partially-cloudy.svg +12 -0
- package/dist/assets/images/icons/partlysunny.svg +6 -0
- package/dist/assets/images/icons/rain-night.svg +8 -0
- package/dist/assets/images/icons/rainy.svg +6 -0
- package/dist/assets/images/icons/sleet-night.svg +14 -0
- package/dist/assets/images/icons/sleet.svg +4 -0
- package/dist/assets/images/icons/snow.svg +19 -0
- package/dist/assets/images/icons/thunderstorm-night.svg +9 -0
- package/dist/assets/images/icons/thunderstorm.svg +7 -0
- package/dist/assets/images/icons/windy.svg +6 -0
- package/dist/assets/images/screenly.svg +10 -0
- package/dist/components/app-header/app-header.d.ts +43 -0
- package/dist/components/app-header/app-header.d.ts.map +1 -0
- package/dist/components/app-header/app-header.js +244 -0
- package/dist/components/auto-scaler/auto-scaler.d.ts +65 -0
- package/dist/components/auto-scaler/auto-scaler.d.ts.map +1 -0
- package/dist/components/auto-scaler/auto-scaler.js +304 -0
- package/dist/components/brand-logo/brand-logo.d.ts +42 -0
- package/dist/components/brand-logo/brand-logo.d.ts.map +1 -0
- package/dist/components/brand-logo/brand-logo.js +209 -0
- package/dist/components/calendar-views/calendar-view-utils.d.ts +5 -0
- package/dist/components/calendar-views/calendar-view-utils.d.ts.map +1 -0
- package/dist/components/calendar-views/calendar-view-utils.js +73 -0
- package/dist/components/calendar-views/calendar-window-utils.d.ts +9 -0
- package/dist/components/calendar-views/calendar-window-utils.d.ts.map +1 -0
- package/dist/components/calendar-views/calendar-window-utils.js +57 -0
- package/dist/components/calendar-views/daily-calendar-view/daily-calendar-view-styles.d.ts +2 -0
- package/dist/components/calendar-views/daily-calendar-view/daily-calendar-view-styles.d.ts.map +1 -0
- package/dist/components/calendar-views/daily-calendar-view/daily-calendar-view-styles.js +175 -0
- package/dist/components/calendar-views/daily-calendar-view/daily-calendar-view.d.ts +21 -0
- package/dist/components/calendar-views/daily-calendar-view/daily-calendar-view.d.ts.map +1 -0
- package/dist/components/calendar-views/daily-calendar-view/daily-calendar-view.js +130 -0
- package/dist/components/calendar-views/daily-calendar-view/index.d.ts +2 -0
- package/dist/components/calendar-views/daily-calendar-view/index.d.ts.map +1 -0
- package/dist/components/calendar-views/daily-calendar-view/index.js +1 -0
- package/dist/components/calendar-views/event-layout.d.ts +18 -0
- package/dist/components/calendar-views/event-layout.d.ts.map +1 -0
- package/dist/components/calendar-views/event-layout.js +139 -0
- package/dist/components/calendar-views/schedule-calendar-view/index.d.ts +2 -0
- package/dist/components/calendar-views/schedule-calendar-view/index.d.ts.map +1 -0
- package/dist/components/calendar-views/schedule-calendar-view/index.js +1 -0
- package/dist/components/calendar-views/schedule-calendar-view/schedule-calendar-view-styles.d.ts +2 -0
- package/dist/components/calendar-views/schedule-calendar-view/schedule-calendar-view-styles.d.ts.map +1 -0
- package/dist/components/calendar-views/schedule-calendar-view/schedule-calendar-view-styles.js +118 -0
- package/dist/components/calendar-views/schedule-calendar-view/schedule-calendar-view.d.ts +23 -0
- package/dist/components/calendar-views/schedule-calendar-view/schedule-calendar-view.d.ts.map +1 -0
- package/dist/components/calendar-views/schedule-calendar-view/schedule-calendar-view.js +167 -0
- package/dist/components/calendar-views/weekly-calendar-view/index.d.ts +3 -0
- package/dist/components/calendar-views/weekly-calendar-view/index.d.ts.map +1 -0
- package/dist/components/calendar-views/weekly-calendar-view/index.js +1 -0
- package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view-styles.d.ts +2 -0
- package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view-styles.d.ts.map +1 -0
- package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view-styles.js +234 -0
- package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view-utils.d.ts +16 -0
- package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view-utils.d.ts.map +1 -0
- package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view-utils.js +83 -0
- package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view.d.ts +26 -0
- package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view.d.ts.map +1 -0
- package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view.js +220 -0
- package/dist/components/dev-tools/dev-tools.d.ts +48 -0
- package/dist/components/dev-tools/dev-tools.d.ts.map +1 -0
- package/dist/components/dev-tools/dev-tools.js +186 -0
- package/dist/components/index.d.ts +14 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +13 -0
- package/dist/components/register.d.ts +12 -0
- package/dist/components/register.d.ts.map +1 -0
- package/dist/components/register.js +11 -0
- package/dist/core/index.d.ts +30 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +77 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/test/index.d.ts +3 -0
- package/dist/test/index.d.ts.map +1 -0
- package/dist/test/index.js +2 -0
- package/dist/test/mock.d.ts +23 -0
- package/dist/test/mock.d.ts.map +1 -0
- package/dist/test/mock.js +49 -0
- package/dist/test/screenshots.d.ts +120 -0
- package/dist/test/screenshots.d.ts.map +1 -0
- package/dist/test/screenshots.js +127 -0
- package/dist/test/setup.d.ts +2 -0
- package/dist/test/setup.d.ts.map +1 -0
- package/dist/test/setup.js +13 -0
- package/dist/types/index.d.ts +138 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +15 -0
- package/dist/utils/calendar.d.ts +28 -0
- package/dist/utils/calendar.d.ts.map +1 -0
- package/dist/utils/calendar.js +77 -0
- package/dist/utils/error-handling.d.ts +6 -0
- package/dist/utils/error-handling.d.ts.map +1 -0
- package/dist/utils/error-handling.js +16 -0
- package/dist/utils/html.d.ts +7 -0
- package/dist/utils/html.d.ts.map +1 -0
- package/dist/utils/html.js +13 -0
- package/dist/utils/index.d.ts +13 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +12 -0
- package/dist/utils/locale.d.ts +68 -0
- package/dist/utils/locale.d.ts.map +1 -0
- package/dist/utils/locale.js +318 -0
- package/dist/utils/metadata.d.ts +39 -0
- package/dist/utils/metadata.d.ts.map +1 -0
- package/dist/utils/metadata.js +66 -0
- package/dist/utils/oauth.d.ts +16 -0
- package/dist/utils/oauth.d.ts.map +1 -0
- package/dist/utils/oauth.js +42 -0
- package/dist/utils/screen.d.ts +26 -0
- package/dist/utils/screen.d.ts.map +1 -0
- package/dist/utils/screen.js +44 -0
- package/dist/utils/settings.d.ts +38 -0
- package/dist/utils/settings.d.ts.map +1 -0
- package/dist/utils/settings.js +89 -0
- package/dist/utils/template.d.ts +2 -0
- package/dist/utils/template.d.ts.map +1 -0
- package/dist/utils/template.js +6 -0
- package/dist/utils/theme.d.ts +47 -0
- package/dist/utils/theme.d.ts.map +1 -0
- package/dist/utils/theme.js +183 -0
- package/dist/utils/utm.d.ts +23 -0
- package/dist/utils/utm.d.ts.map +1 -0
- package/dist/utils/utm.js +30 -0
- package/dist/utils/weather.d.ts +84 -0
- package/dist/utils/weather.d.ts.map +1 -0
- package/dist/utils/weather.js +272 -0
- package/package.json +8 -7
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* <auto-scaler>
|
|
3
|
+
*
|
|
4
|
+
* Web component that automatically scales its slotted content from a reference
|
|
5
|
+
* resolution to the current viewport.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* ```html
|
|
9
|
+
* <auto-scaler
|
|
10
|
+
* reference-width="1920"
|
|
11
|
+
* reference-height="1080"
|
|
12
|
+
* orientation="auto"
|
|
13
|
+
* center-content
|
|
14
|
+
* >
|
|
15
|
+
* <!-- Your app content sized for 1920×1080 -->
|
|
16
|
+
* </auto-scaler>
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export class AutoScalerElement extends HTMLElement {
|
|
20
|
+
currentScale = 1;
|
|
21
|
+
resizeObserver;
|
|
22
|
+
resizeTimeout;
|
|
23
|
+
boundDebouncedResize = () => this.debouncedResize();
|
|
24
|
+
// Internal options derived from attributes
|
|
25
|
+
_referenceWidth;
|
|
26
|
+
_referenceHeight;
|
|
27
|
+
_orientation = 'auto';
|
|
28
|
+
_centerContent = true;
|
|
29
|
+
_padding = '20';
|
|
30
|
+
_debounceMs = 100;
|
|
31
|
+
static get observedAttributes() {
|
|
32
|
+
return [
|
|
33
|
+
'reference-width',
|
|
34
|
+
'reference-height',
|
|
35
|
+
'orientation',
|
|
36
|
+
'center-content',
|
|
37
|
+
'padding',
|
|
38
|
+
'debounce-ms',
|
|
39
|
+
];
|
|
40
|
+
}
|
|
41
|
+
constructor() {
|
|
42
|
+
super();
|
|
43
|
+
// Create shadow root for encapsulated styles (best practice)
|
|
44
|
+
const shadow = this.attachShadow({ mode: 'open' });
|
|
45
|
+
const style = document.createElement('style');
|
|
46
|
+
style.textContent = `
|
|
47
|
+
:host {
|
|
48
|
+
display: block;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
:host([hidden]) {
|
|
52
|
+
display: none;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
::slotted(*) {
|
|
56
|
+
/* Slotted content can assume a 1920×1080 canvas; AutoScaler rescales host */
|
|
57
|
+
}
|
|
58
|
+
`;
|
|
59
|
+
const slot = document.createElement('slot');
|
|
60
|
+
shadow.append(style, slot);
|
|
61
|
+
}
|
|
62
|
+
connectedCallback() {
|
|
63
|
+
// Handle properties that may have been set before upgrade
|
|
64
|
+
this.upgradeProperty('referenceWidth');
|
|
65
|
+
this.upgradeProperty('referenceHeight');
|
|
66
|
+
this.loadAttributes();
|
|
67
|
+
this.init();
|
|
68
|
+
}
|
|
69
|
+
disconnectedCallback() {
|
|
70
|
+
this.teardown();
|
|
71
|
+
}
|
|
72
|
+
attributeChangedCallback(name, _oldValue, _newValue) {
|
|
73
|
+
if (!this.isConnected)
|
|
74
|
+
return;
|
|
75
|
+
// Recalculate when configuration changes
|
|
76
|
+
if (AutoScalerElement.observedAttributes.includes(name)) {
|
|
77
|
+
this.loadAttributes();
|
|
78
|
+
this.init();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Upgrade any properties that were set on the instance before
|
|
83
|
+
* the custom element definition was loaded.
|
|
84
|
+
* See: https://web.dev/articles/custom-elements-best-practices
|
|
85
|
+
*/
|
|
86
|
+
upgradeProperty(prop) {
|
|
87
|
+
if (Object.prototype.hasOwnProperty.call(this, prop)) {
|
|
88
|
+
const element = this;
|
|
89
|
+
const value = element[prop];
|
|
90
|
+
delete element[prop];
|
|
91
|
+
element[prop] = value;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
loadAttributes() {
|
|
95
|
+
const refWidthAttr = this.getAttribute('reference-width');
|
|
96
|
+
const refHeightAttr = this.getAttribute('reference-height');
|
|
97
|
+
if (refWidthAttr && refHeightAttr) {
|
|
98
|
+
const referenceWidth = Number(refWidthAttr);
|
|
99
|
+
const referenceHeight = Number(refHeightAttr);
|
|
100
|
+
if (Number.isFinite(referenceWidth) && Number.isFinite(referenceHeight)) {
|
|
101
|
+
this._referenceWidth = referenceWidth;
|
|
102
|
+
this._referenceHeight = referenceHeight;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const orientationAttr = this.getAttribute('orientation');
|
|
106
|
+
if (orientationAttr === 'landscape' ||
|
|
107
|
+
orientationAttr === 'portrait' ||
|
|
108
|
+
orientationAttr === 'auto') {
|
|
109
|
+
this._orientation = orientationAttr;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this._orientation = 'auto';
|
|
113
|
+
}
|
|
114
|
+
this._centerContent = this.hasAttribute('center-content');
|
|
115
|
+
const paddingAttr = this.getAttribute('padding');
|
|
116
|
+
if (paddingAttr && Number.isFinite(Number(paddingAttr))) {
|
|
117
|
+
this._padding = String(Number(paddingAttr));
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
this._padding = '20';
|
|
121
|
+
}
|
|
122
|
+
const debounceMsAttr = this.getAttribute('debounce-ms');
|
|
123
|
+
if (debounceMsAttr && Number.isFinite(Number(debounceMsAttr))) {
|
|
124
|
+
this._debounceMs = Number(debounceMsAttr);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
this._debounceMs = 100;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
init() {
|
|
131
|
+
if (!this._referenceWidth || !this._referenceHeight) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// Set initial container styles
|
|
135
|
+
this.style.width = `${this._referenceWidth}px`;
|
|
136
|
+
this.style.height = `${this._referenceHeight}px`;
|
|
137
|
+
this.style.padding = `${this._padding}px`;
|
|
138
|
+
this.style.position = 'fixed';
|
|
139
|
+
this.style.top = '0px';
|
|
140
|
+
this.style.transformOrigin = 'top left';
|
|
141
|
+
this.style.overflow = 'hidden';
|
|
142
|
+
// Calculate and apply initial scale
|
|
143
|
+
this.calculateAndApplyScale();
|
|
144
|
+
// Set up resize handling
|
|
145
|
+
this.setupResizeHandling();
|
|
146
|
+
}
|
|
147
|
+
calculateAndApplyScale() {
|
|
148
|
+
if (!this._referenceWidth || !this._referenceHeight) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const viewportWidth = window.innerWidth;
|
|
152
|
+
const viewportHeight = window.innerHeight;
|
|
153
|
+
// Determine orientation
|
|
154
|
+
let orientation;
|
|
155
|
+
if (this._orientation === 'auto' || !this._orientation) {
|
|
156
|
+
orientation = viewportWidth >= viewportHeight ? 'landscape' : 'portrait';
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
orientation = this._orientation;
|
|
160
|
+
}
|
|
161
|
+
// Set container dimensions based on orientation
|
|
162
|
+
let effectiveWidth;
|
|
163
|
+
if (orientation === 'portrait') {
|
|
164
|
+
// Swap dimensions for portrait
|
|
165
|
+
this.style.width = `${this._referenceHeight}px`;
|
|
166
|
+
this.style.height = `${this._referenceWidth}px`;
|
|
167
|
+
effectiveWidth = this._referenceHeight;
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
// Use original dimensions for landscape
|
|
171
|
+
this.style.width = `${this._referenceWidth}px`;
|
|
172
|
+
this.style.height = `${this._referenceHeight}px`;
|
|
173
|
+
effectiveWidth = this._referenceWidth;
|
|
174
|
+
}
|
|
175
|
+
// Set CSS variable for signage unit (su)
|
|
176
|
+
// 1su = 1% of reference width (e.g., 1920px / 100 = 19.2px)
|
|
177
|
+
const oneSU = effectiveWidth / 100;
|
|
178
|
+
this.style.setProperty('--su', `${oneSU}px`);
|
|
179
|
+
// Calculate scale based on orientation
|
|
180
|
+
let scaleX;
|
|
181
|
+
let scaleY;
|
|
182
|
+
if (orientation === 'landscape') {
|
|
183
|
+
scaleX = viewportWidth / this._referenceWidth;
|
|
184
|
+
scaleY = viewportHeight / this._referenceHeight;
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
// Portrait mode - swap reference dimensions
|
|
188
|
+
scaleX = viewportWidth / this._referenceHeight;
|
|
189
|
+
scaleY = viewportHeight / this._referenceWidth;
|
|
190
|
+
}
|
|
191
|
+
// Use the smaller scale to ensure content fits
|
|
192
|
+
const scale = Math.min(scaleX, scaleY);
|
|
193
|
+
// Apply scale transform
|
|
194
|
+
this.style.transform = `scale(${scale})`;
|
|
195
|
+
// Calculate scaled dimensions (swap in portrait mode)
|
|
196
|
+
let scaledWidth;
|
|
197
|
+
if (orientation === 'portrait') {
|
|
198
|
+
scaledWidth = this._referenceHeight * scale;
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
scaledWidth = this._referenceWidth * scale;
|
|
202
|
+
}
|
|
203
|
+
// Always center horizontally when scaled content is smaller than viewport
|
|
204
|
+
// This ensures content is centered for narrow screens like 480x800 portrait
|
|
205
|
+
if (scaledWidth < viewportWidth) {
|
|
206
|
+
const offsetX = (viewportWidth - scaledWidth) / 2;
|
|
207
|
+
this.style.left = `${offsetX}px`;
|
|
208
|
+
}
|
|
209
|
+
else if (this._centerContent) {
|
|
210
|
+
// If center-content is explicitly set, center even when content is larger
|
|
211
|
+
const offsetX = (viewportWidth - scaledWidth) / 2;
|
|
212
|
+
this.style.left = `${offsetX}px`;
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
this.style.left = '0px';
|
|
216
|
+
}
|
|
217
|
+
// Dispatch event and update scale if changed
|
|
218
|
+
if (scale !== this.currentScale) {
|
|
219
|
+
this.currentScale = scale;
|
|
220
|
+
this.dispatchEvent(new CustomEvent('scalechange', {
|
|
221
|
+
detail: { scale },
|
|
222
|
+
bubbles: true,
|
|
223
|
+
composed: true,
|
|
224
|
+
}));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
setupResizeHandling() {
|
|
228
|
+
this.teardownResizeHandling();
|
|
229
|
+
// Use ResizeObserver if available, fallback to window resize
|
|
230
|
+
if (typeof ResizeObserver !== 'undefined') {
|
|
231
|
+
this.resizeObserver = new ResizeObserver(() => {
|
|
232
|
+
this.debouncedResize();
|
|
233
|
+
});
|
|
234
|
+
this.resizeObserver.observe(document.body);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
window.addEventListener('resize', this.boundDebouncedResize);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
debouncedResize() {
|
|
241
|
+
if (this.resizeTimeout) {
|
|
242
|
+
clearTimeout(this.resizeTimeout);
|
|
243
|
+
}
|
|
244
|
+
this.resizeTimeout = window.setTimeout(() => {
|
|
245
|
+
this.calculateAndApplyScale();
|
|
246
|
+
}, this._debounceMs);
|
|
247
|
+
}
|
|
248
|
+
teardownResizeHandling() {
|
|
249
|
+
if (this.resizeObserver) {
|
|
250
|
+
this.resizeObserver.disconnect();
|
|
251
|
+
this.resizeObserver = undefined;
|
|
252
|
+
}
|
|
253
|
+
if (this.resizeTimeout) {
|
|
254
|
+
clearTimeout(this.resizeTimeout);
|
|
255
|
+
this.resizeTimeout = undefined;
|
|
256
|
+
}
|
|
257
|
+
window.removeEventListener('resize', this.boundDebouncedResize);
|
|
258
|
+
}
|
|
259
|
+
teardown() {
|
|
260
|
+
this.teardownResizeHandling();
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Public API: get current scale factor
|
|
264
|
+
*/
|
|
265
|
+
get scale() {
|
|
266
|
+
return this.currentScale;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Manually trigger scale recalculation
|
|
270
|
+
*/
|
|
271
|
+
recalculate() {
|
|
272
|
+
this.calculateAndApplyScale();
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Properties that reflect to attributes (primitive data).
|
|
276
|
+
* Keeping them in sync matches platform behavior.
|
|
277
|
+
*/
|
|
278
|
+
get referenceWidth() {
|
|
279
|
+
return this._referenceWidth;
|
|
280
|
+
}
|
|
281
|
+
set referenceWidth(value) {
|
|
282
|
+
if (value === undefined || value === null) {
|
|
283
|
+
this.removeAttribute('reference-width');
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
this.setAttribute('reference-width', String(value));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
get referenceHeight() {
|
|
290
|
+
return this._referenceHeight;
|
|
291
|
+
}
|
|
292
|
+
set referenceHeight(value) {
|
|
293
|
+
if (value === undefined || value === null) {
|
|
294
|
+
this.removeAttribute('reference-height');
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
this.setAttribute('reference-height', String(value));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
// Register the custom element
|
|
302
|
+
if (typeof window !== 'undefined' && !customElements.get('auto-scaler')) {
|
|
303
|
+
customElements.define('auto-scaler', AutoScalerElement);
|
|
304
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BrandLogo Web Component
|
|
3
|
+
* Displays the branding logo from Screenly settings with fallback to screen name
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* ```html
|
|
7
|
+
* <brand-logo></brand-logo>
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* Attributes:
|
|
11
|
+
* - `show-name` (optional): Show screen name alongside logo (default: false)
|
|
12
|
+
* - `fallback-to-name` (optional): Show screen name if logo unavailable (default: true)
|
|
13
|
+
* - `max-width` (optional): Maximum width for logo (default: "120px")
|
|
14
|
+
* - `max-height` (optional): Maximum height for logo (default: "32px")
|
|
15
|
+
*/
|
|
16
|
+
export declare class BrandLogo extends HTMLElement {
|
|
17
|
+
private logoUrl;
|
|
18
|
+
private _showName;
|
|
19
|
+
private _fallbackToName;
|
|
20
|
+
private _maxWidth;
|
|
21
|
+
private _maxHeight;
|
|
22
|
+
static get observedAttributes(): string[];
|
|
23
|
+
constructor();
|
|
24
|
+
connectedCallback(): void;
|
|
25
|
+
attributeChangedCallback(): void;
|
|
26
|
+
private upgradeProperty;
|
|
27
|
+
private loadAttributes;
|
|
28
|
+
private updateCssProperties;
|
|
29
|
+
private loadLogo;
|
|
30
|
+
private updateLogo;
|
|
31
|
+
private updateFallback;
|
|
32
|
+
private render;
|
|
33
|
+
get showName(): boolean;
|
|
34
|
+
set showName(value: boolean);
|
|
35
|
+
get fallbackToName(): boolean;
|
|
36
|
+
set fallbackToName(value: boolean);
|
|
37
|
+
get maxWidth(): string;
|
|
38
|
+
set maxWidth(value: string);
|
|
39
|
+
get maxHeight(): string;
|
|
40
|
+
set maxHeight(value: string);
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=brand-logo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand-logo.d.ts","sourceRoot":"","sources":["../../../src/components/brand-logo/brand-logo.ts"],"names":[],"mappings":"AAmDA;;;;;;;;;;;;;;GAcG;AACH,qBAAa,SAAU,SAAQ,WAAW;IACxC,OAAO,CAAC,OAAO,CAAa;IAC5B,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,eAAe,CAAgB;IACvC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,UAAU,CAAiB;IAEnC,MAAM,KAAK,kBAAkB,aAE5B;;IAOD,iBAAiB;IAWjB,wBAAwB;IAKxB,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,cAAc;IAQtB,OAAO,CAAC,mBAAmB;YAKb,QAAQ;IAkBtB,OAAO,CAAC,UAAU;IAoBlB,OAAO,CAAC,cAAc;IAkBtB,OAAO,CAAC,MAAM;IAed,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,IAAI,QAAQ,CAAC,KAAK,EAAE,OAAO,EAM1B;IAED,IAAI,cAAc,IAAI,OAAO,CAE5B;IAED,IAAI,cAAc,CAAC,KAAK,EAAE,OAAO,EAEhC;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,IAAI,QAAQ,CAAC,KAAK,EAAE,MAAM,EAMzB;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,SAAS,CAAC,KAAK,EAAE,MAAM,EAM1B;CACF"}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { setupBrandingLogo, getScreenName } from '../../utils/index.js';
|
|
2
|
+
const htmlTemplate = `
|
|
3
|
+
<img alt="Brand Logo" />
|
|
4
|
+
<span class="brand-name"></span>
|
|
5
|
+
`;
|
|
6
|
+
const cssTemplate = `
|
|
7
|
+
:host {
|
|
8
|
+
display: inline-flex;
|
|
9
|
+
align-items: center;
|
|
10
|
+
gap: 0.75rem;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
:host([hidden]) {
|
|
14
|
+
display: none;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
img {
|
|
18
|
+
height: var(--brand-logo-height, 32px);
|
|
19
|
+
width: auto;
|
|
20
|
+
max-width: var(--brand-logo-max-width, 120px);
|
|
21
|
+
object-fit: contain;
|
|
22
|
+
vertical-align: middle;
|
|
23
|
+
padding-right: 0.75rem;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
img[src=""],
|
|
27
|
+
img:not([src]) {
|
|
28
|
+
display: none;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.brand-name {
|
|
32
|
+
font-weight: 500;
|
|
33
|
+
font-size: 1.25rem;
|
|
34
|
+
letter-spacing: 0.025em;
|
|
35
|
+
white-space: nowrap;
|
|
36
|
+
vertical-align: middle;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.brand-name:empty {
|
|
40
|
+
display: none;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@media (orientation: portrait) {
|
|
44
|
+
.brand-name {
|
|
45
|
+
font-size: 1.5rem;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
/**
|
|
50
|
+
* BrandLogo Web Component
|
|
51
|
+
* Displays the branding logo from Screenly settings with fallback to screen name
|
|
52
|
+
*
|
|
53
|
+
* Usage:
|
|
54
|
+
* ```html
|
|
55
|
+
* <brand-logo></brand-logo>
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* Attributes:
|
|
59
|
+
* - `show-name` (optional): Show screen name alongside logo (default: false)
|
|
60
|
+
* - `fallback-to-name` (optional): Show screen name if logo unavailable (default: true)
|
|
61
|
+
* - `max-width` (optional): Maximum width for logo (default: "120px")
|
|
62
|
+
* - `max-height` (optional): Maximum height for logo (default: "32px")
|
|
63
|
+
*/
|
|
64
|
+
export class BrandLogo extends HTMLElement {
|
|
65
|
+
logoUrl = '';
|
|
66
|
+
_showName = false;
|
|
67
|
+
_fallbackToName = true;
|
|
68
|
+
_maxWidth = '120px';
|
|
69
|
+
_maxHeight = '32px';
|
|
70
|
+
static get observedAttributes() {
|
|
71
|
+
return ['show-name', 'fallback-to-name', 'max-width', 'max-height'];
|
|
72
|
+
}
|
|
73
|
+
constructor() {
|
|
74
|
+
super();
|
|
75
|
+
this.attachShadow({ mode: 'open' });
|
|
76
|
+
}
|
|
77
|
+
connectedCallback() {
|
|
78
|
+
this.upgradeProperty('showName');
|
|
79
|
+
this.upgradeProperty('fallbackToName');
|
|
80
|
+
this.upgradeProperty('maxWidth');
|
|
81
|
+
this.upgradeProperty('maxHeight');
|
|
82
|
+
this.loadAttributes();
|
|
83
|
+
this.render();
|
|
84
|
+
this.loadLogo();
|
|
85
|
+
}
|
|
86
|
+
attributeChangedCallback() {
|
|
87
|
+
this.loadAttributes();
|
|
88
|
+
this.render();
|
|
89
|
+
}
|
|
90
|
+
upgradeProperty(prop) {
|
|
91
|
+
if (Object.prototype.hasOwnProperty.call(this, prop)) {
|
|
92
|
+
const element = this;
|
|
93
|
+
const value = element[prop];
|
|
94
|
+
delete element[prop];
|
|
95
|
+
element[prop] = value;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
loadAttributes() {
|
|
99
|
+
this._showName = this.hasAttribute('show-name');
|
|
100
|
+
this._fallbackToName = this.getAttribute('fallback-to-name') !== 'false';
|
|
101
|
+
this._maxWidth = this.getAttribute('max-width') || '120px';
|
|
102
|
+
this._maxHeight = this.getAttribute('max-height') || '32px';
|
|
103
|
+
this.updateCssProperties();
|
|
104
|
+
}
|
|
105
|
+
updateCssProperties() {
|
|
106
|
+
this.style.setProperty('--brand-logo-height', this._maxHeight);
|
|
107
|
+
this.style.setProperty('--brand-logo-max-width', this._maxWidth);
|
|
108
|
+
}
|
|
109
|
+
async loadLogo() {
|
|
110
|
+
try {
|
|
111
|
+
const logoUrl = await setupBrandingLogo();
|
|
112
|
+
this.logoUrl = logoUrl;
|
|
113
|
+
if (logoUrl) {
|
|
114
|
+
this.updateLogo();
|
|
115
|
+
}
|
|
116
|
+
else if (this.fallbackToName) {
|
|
117
|
+
this.updateFallback();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
catch (error) {
|
|
121
|
+
console.warn('Failed to load branding logo:', error);
|
|
122
|
+
if (this.fallbackToName) {
|
|
123
|
+
this.updateFallback();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
updateLogo() {
|
|
128
|
+
const img = this.shadowRoot.querySelector('img');
|
|
129
|
+
const nameEl = this.shadowRoot.querySelector('.brand-name');
|
|
130
|
+
if (img) {
|
|
131
|
+
img.src = this.logoUrl;
|
|
132
|
+
img.style.removeProperty('display');
|
|
133
|
+
}
|
|
134
|
+
if (nameEl && this._showName) {
|
|
135
|
+
const screenName = getScreenName();
|
|
136
|
+
if (screenName) {
|
|
137
|
+
nameEl.textContent = screenName;
|
|
138
|
+
nameEl.style.removeProperty('display');
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
updateFallback() {
|
|
143
|
+
const img = this.shadowRoot.querySelector('img');
|
|
144
|
+
const nameEl = this.shadowRoot.querySelector('.brand-name');
|
|
145
|
+
if (img) {
|
|
146
|
+
img.removeAttribute('src');
|
|
147
|
+
}
|
|
148
|
+
if (nameEl) {
|
|
149
|
+
const screenName = getScreenName();
|
|
150
|
+
if (screenName) {
|
|
151
|
+
nameEl.textContent = screenName;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
render() {
|
|
156
|
+
const style = this.shadowRoot.querySelector('style');
|
|
157
|
+
if (!style) {
|
|
158
|
+
// First render
|
|
159
|
+
this.shadowRoot.innerHTML = `
|
|
160
|
+
<style>${cssTemplate}</style>
|
|
161
|
+
${htmlTemplate}
|
|
162
|
+
`;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Public properties reflecting to attributes (primitive data)
|
|
166
|
+
get showName() {
|
|
167
|
+
return this._showName;
|
|
168
|
+
}
|
|
169
|
+
set showName(value) {
|
|
170
|
+
if (value) {
|
|
171
|
+
this.setAttribute('show-name', '');
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
this.removeAttribute('show-name');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
get fallbackToName() {
|
|
178
|
+
return this._fallbackToName;
|
|
179
|
+
}
|
|
180
|
+
set fallbackToName(value) {
|
|
181
|
+
this.setAttribute('fallback-to-name', value ? 'true' : 'false');
|
|
182
|
+
}
|
|
183
|
+
get maxWidth() {
|
|
184
|
+
return this._maxWidth;
|
|
185
|
+
}
|
|
186
|
+
set maxWidth(value) {
|
|
187
|
+
if (!value) {
|
|
188
|
+
this.removeAttribute('max-width');
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
this.setAttribute('max-width', value);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
get maxHeight() {
|
|
195
|
+
return this._maxHeight;
|
|
196
|
+
}
|
|
197
|
+
set maxHeight(value) {
|
|
198
|
+
if (!value) {
|
|
199
|
+
this.removeAttribute('max-height');
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
this.setAttribute('max-height', value);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Register the custom element
|
|
207
|
+
if (typeof window !== 'undefined' && !customElements.get('brand-logo')) {
|
|
208
|
+
customElements.define('brand-logo', BrandLogo);
|
|
209
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CalendarEvent, EventLayout } from './event-layout.js';
|
|
2
|
+
import type { TimeSlot } from './calendar-window-utils.js';
|
|
3
|
+
export declare function buildTimeGutter(timeSlots: TimeSlot[]): HTMLElement;
|
|
4
|
+
export declare function buildEventElement(event: CalendarEvent, windowStartHour: number, layout: EventLayout, locale: string, timezone: string): HTMLElement;
|
|
5
|
+
//# sourceMappingURL=calendar-view-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar-view-utils.d.ts","sourceRoot":"","sources":["../../../src/components/calendar-views/calendar-view-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAO1D,wBAAgB,eAAe,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,WAAW,CAUlE;AAgED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,aAAa,EACpB,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,WAAW,CA0Bb"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { getEventStyle, formatEventStartTime, formatEventTime, } from './weekly-calendar-view/weekly-calendar-view-utils.js';
|
|
2
|
+
export function buildTimeGutter(timeSlots) {
|
|
3
|
+
const timeGutter = document.createElement('div');
|
|
4
|
+
timeGutter.className = 'time-gutter';
|
|
5
|
+
for (const slot of timeSlots) {
|
|
6
|
+
const label = document.createElement('div');
|
|
7
|
+
label.className = 'time-label';
|
|
8
|
+
label.textContent = slot.time;
|
|
9
|
+
timeGutter.appendChild(label);
|
|
10
|
+
}
|
|
11
|
+
return timeGutter;
|
|
12
|
+
}
|
|
13
|
+
function buildCompactEventItem(event, locale, timezone) {
|
|
14
|
+
const item = document.createElement('div');
|
|
15
|
+
item.className = 'event-item event-compact';
|
|
16
|
+
if (event.backgroundColor) {
|
|
17
|
+
item.style.setProperty('background-color', event.backgroundColor);
|
|
18
|
+
}
|
|
19
|
+
const titleEl = document.createElement('div');
|
|
20
|
+
titleEl.className = 'event-title';
|
|
21
|
+
titleEl.textContent = `${event.title}, `;
|
|
22
|
+
const inlineTimeEl = document.createElement('span');
|
|
23
|
+
inlineTimeEl.className = 'event-inline-time';
|
|
24
|
+
inlineTimeEl.textContent = formatEventStartTime(event.startTime, locale, timezone);
|
|
25
|
+
titleEl.appendChild(inlineTimeEl);
|
|
26
|
+
item.appendChild(titleEl);
|
|
27
|
+
return item;
|
|
28
|
+
}
|
|
29
|
+
function buildFullEventItem(event, style, locale, timezone) {
|
|
30
|
+
const item = document.createElement('div');
|
|
31
|
+
const itemClasses = ['event-item'];
|
|
32
|
+
if (style.clippedTop)
|
|
33
|
+
itemClasses.push('clipped-top');
|
|
34
|
+
if (style.clippedBottom)
|
|
35
|
+
itemClasses.push('clipped-bottom');
|
|
36
|
+
item.className = itemClasses.join(' ');
|
|
37
|
+
if (event.backgroundColor) {
|
|
38
|
+
item.style.setProperty('background-color', event.backgroundColor);
|
|
39
|
+
}
|
|
40
|
+
const titleEl = document.createElement('div');
|
|
41
|
+
titleEl.className = 'event-title';
|
|
42
|
+
titleEl.textContent = event.title;
|
|
43
|
+
const timeEl = document.createElement('div');
|
|
44
|
+
timeEl.className = 'event-time';
|
|
45
|
+
timeEl.textContent = formatEventTime(event.startTime, event.endTime, locale, timezone);
|
|
46
|
+
item.appendChild(titleEl);
|
|
47
|
+
item.appendChild(timeEl);
|
|
48
|
+
return item;
|
|
49
|
+
}
|
|
50
|
+
export function buildEventElement(event, windowStartHour, layout, locale, timezone) {
|
|
51
|
+
const style = getEventStyle(event, windowStartHour, layout, timezone);
|
|
52
|
+
const wrapper = document.createElement('div');
|
|
53
|
+
wrapper.className = 'event-wrapper';
|
|
54
|
+
wrapper.style.setProperty('top', `${style.topPercent}%`);
|
|
55
|
+
wrapper.style.setProperty('height', `${style.heightPercent}%`);
|
|
56
|
+
wrapper.style.setProperty('left', `${style.leftPercent}%`);
|
|
57
|
+
wrapper.style.setProperty('z-index', String(style.zIndex));
|
|
58
|
+
if (style.isLastColumn) {
|
|
59
|
+
wrapper.style.setProperty('right', '0');
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
wrapper.style.setProperty('width', `${style.widthPercent}%`);
|
|
63
|
+
}
|
|
64
|
+
// Events shorter than 45 minutes don't have room for a stacked title + time range,
|
|
65
|
+
// so they get the compact inline layout instead.
|
|
66
|
+
const COMPACT_THRESHOLD_PERCENT = (45 / 60) * (100 / 12);
|
|
67
|
+
const isCompact = style.heightPercent <= COMPACT_THRESHOLD_PERCENT;
|
|
68
|
+
const item = isCompact
|
|
69
|
+
? buildCompactEventItem(event, locale, timezone)
|
|
70
|
+
: buildFullEventItem(event, style, locale, timezone);
|
|
71
|
+
wrapper.appendChild(item);
|
|
72
|
+
return wrapper;
|
|
73
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CalendarEvent } from './event-layout.js';
|
|
2
|
+
export interface TimeSlot {
|
|
3
|
+
time: string;
|
|
4
|
+
hour: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function getWindowStartHour(currentHour: number): number;
|
|
7
|
+
export declare function generateTimeSlots(startHour: number, now: Date, locale: string, timezone: string): TimeSlot[];
|
|
8
|
+
export declare function filterEventsForWindow(events: CalendarEvent[], dayDateStr: string, windowStartHour: number, timezone: string): CalendarEvent[];
|
|
9
|
+
//# sourceMappingURL=calendar-window-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"calendar-window-utils.d.ts","sourceRoot":"","sources":["../../../src/components/calendar-views/calendar-window-utils.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAKtD,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb;AAKD,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,IAAI,EACT,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,QAAQ,EAAE,CA0BZ;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,aAAa,EAAE,EACvB,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,GACf,aAAa,EAAE,CAgBjB"}
|