@screenly/edge-apps 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/dist/assets/fonts/Inter-Medium.woff2 +0 -0
  2. package/dist/assets/fonts/Inter-Regular.woff2 +0 -0
  3. package/dist/assets/fonts/Inter-SemiBold.woff2 +0 -0
  4. package/dist/assets/images/icons/chancesleet.svg +4 -0
  5. package/dist/assets/images/icons/clear-night.svg +5 -0
  6. package/dist/assets/images/icons/clear.svg +11 -0
  7. package/dist/assets/images/icons/cloudy.svg +4 -0
  8. package/dist/assets/images/icons/drizzle.svg +5 -0
  9. package/dist/assets/images/icons/fewdrops.svg +4 -0
  10. package/dist/assets/images/icons/fog.svg +6 -0
  11. package/dist/assets/images/icons/haze.svg +6 -0
  12. package/dist/assets/images/icons/mostly-cloudy-night.svg +7 -0
  13. package/dist/assets/images/icons/mostly-cloudy.svg +13 -0
  14. package/dist/assets/images/icons/partially-cloudy-night.svg +6 -0
  15. package/dist/assets/images/icons/partially-cloudy.svg +12 -0
  16. package/dist/assets/images/icons/partlysunny.svg +6 -0
  17. package/dist/assets/images/icons/rain-night.svg +8 -0
  18. package/dist/assets/images/icons/rainy.svg +6 -0
  19. package/dist/assets/images/icons/sleet-night.svg +14 -0
  20. package/dist/assets/images/icons/sleet.svg +4 -0
  21. package/dist/assets/images/icons/snow.svg +19 -0
  22. package/dist/assets/images/icons/thunderstorm-night.svg +9 -0
  23. package/dist/assets/images/icons/thunderstorm.svg +7 -0
  24. package/dist/assets/images/icons/windy.svg +6 -0
  25. package/dist/assets/images/screenly.svg +10 -0
  26. package/dist/components/app-header/app-header.d.ts +43 -0
  27. package/dist/components/app-header/app-header.d.ts.map +1 -0
  28. package/dist/components/app-header/app-header.js +244 -0
  29. package/dist/components/auto-scaler/auto-scaler.d.ts +65 -0
  30. package/dist/components/auto-scaler/auto-scaler.d.ts.map +1 -0
  31. package/dist/components/auto-scaler/auto-scaler.js +304 -0
  32. package/dist/components/brand-logo/brand-logo.d.ts +42 -0
  33. package/dist/components/brand-logo/brand-logo.d.ts.map +1 -0
  34. package/dist/components/brand-logo/brand-logo.js +209 -0
  35. package/dist/components/calendar-views/calendar-view-utils.d.ts +5 -0
  36. package/dist/components/calendar-views/calendar-view-utils.d.ts.map +1 -0
  37. package/dist/components/calendar-views/calendar-view-utils.js +73 -0
  38. package/dist/components/calendar-views/calendar-window-utils.d.ts +9 -0
  39. package/dist/components/calendar-views/calendar-window-utils.d.ts.map +1 -0
  40. package/dist/components/calendar-views/calendar-window-utils.js +57 -0
  41. package/dist/components/calendar-views/daily-calendar-view/daily-calendar-view-styles.d.ts +2 -0
  42. package/dist/components/calendar-views/daily-calendar-view/daily-calendar-view-styles.d.ts.map +1 -0
  43. package/dist/components/calendar-views/daily-calendar-view/daily-calendar-view-styles.js +175 -0
  44. package/dist/components/calendar-views/daily-calendar-view/daily-calendar-view.d.ts +21 -0
  45. package/dist/components/calendar-views/daily-calendar-view/daily-calendar-view.d.ts.map +1 -0
  46. package/dist/components/calendar-views/daily-calendar-view/daily-calendar-view.js +130 -0
  47. package/dist/components/calendar-views/daily-calendar-view/index.d.ts +2 -0
  48. package/dist/components/calendar-views/daily-calendar-view/index.d.ts.map +1 -0
  49. package/dist/components/calendar-views/daily-calendar-view/index.js +1 -0
  50. package/dist/components/calendar-views/event-layout.d.ts +18 -0
  51. package/dist/components/calendar-views/event-layout.d.ts.map +1 -0
  52. package/dist/components/calendar-views/event-layout.js +139 -0
  53. package/dist/components/calendar-views/schedule-calendar-view/index.d.ts +2 -0
  54. package/dist/components/calendar-views/schedule-calendar-view/index.d.ts.map +1 -0
  55. package/dist/components/calendar-views/schedule-calendar-view/index.js +1 -0
  56. package/dist/components/calendar-views/schedule-calendar-view/schedule-calendar-view-styles.d.ts +2 -0
  57. package/dist/components/calendar-views/schedule-calendar-view/schedule-calendar-view-styles.d.ts.map +1 -0
  58. package/dist/components/calendar-views/schedule-calendar-view/schedule-calendar-view-styles.js +118 -0
  59. package/dist/components/calendar-views/schedule-calendar-view/schedule-calendar-view.d.ts +23 -0
  60. package/dist/components/calendar-views/schedule-calendar-view/schedule-calendar-view.d.ts.map +1 -0
  61. package/dist/components/calendar-views/schedule-calendar-view/schedule-calendar-view.js +167 -0
  62. package/dist/components/calendar-views/weekly-calendar-view/index.d.ts +3 -0
  63. package/dist/components/calendar-views/weekly-calendar-view/index.d.ts.map +1 -0
  64. package/dist/components/calendar-views/weekly-calendar-view/index.js +1 -0
  65. package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view-styles.d.ts +2 -0
  66. package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view-styles.d.ts.map +1 -0
  67. package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view-styles.js +234 -0
  68. package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view-utils.d.ts +16 -0
  69. package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view-utils.d.ts.map +1 -0
  70. package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view-utils.js +83 -0
  71. package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view.d.ts +26 -0
  72. package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view.d.ts.map +1 -0
  73. package/dist/components/calendar-views/weekly-calendar-view/weekly-calendar-view.js +220 -0
  74. package/dist/components/dev-tools/dev-tools.d.ts +48 -0
  75. package/dist/components/dev-tools/dev-tools.d.ts.map +1 -0
  76. package/dist/components/dev-tools/dev-tools.js +186 -0
  77. package/dist/components/index.d.ts +14 -0
  78. package/dist/components/index.d.ts.map +1 -0
  79. package/dist/components/index.js +13 -0
  80. package/dist/components/register.d.ts +12 -0
  81. package/dist/components/register.d.ts.map +1 -0
  82. package/dist/components/register.js +11 -0
  83. package/dist/core/index.d.ts +30 -0
  84. package/dist/core/index.d.ts.map +1 -0
  85. package/dist/core/index.js +77 -0
  86. package/dist/index.d.ts +5 -0
  87. package/dist/index.d.ts.map +1 -0
  88. package/dist/index.js +8 -0
  89. package/dist/test/index.d.ts +3 -0
  90. package/dist/test/index.d.ts.map +1 -0
  91. package/dist/test/index.js +2 -0
  92. package/dist/test/mock.d.ts +23 -0
  93. package/dist/test/mock.d.ts.map +1 -0
  94. package/dist/test/mock.js +49 -0
  95. package/dist/test/screenshots.d.ts +120 -0
  96. package/dist/test/screenshots.d.ts.map +1 -0
  97. package/dist/test/screenshots.js +127 -0
  98. package/dist/test/setup.d.ts +2 -0
  99. package/dist/test/setup.d.ts.map +1 -0
  100. package/dist/test/setup.js +13 -0
  101. package/dist/types/index.d.ts +138 -0
  102. package/dist/types/index.d.ts.map +1 -0
  103. package/dist/types/index.js +15 -0
  104. package/dist/utils/calendar.d.ts +28 -0
  105. package/dist/utils/calendar.d.ts.map +1 -0
  106. package/dist/utils/calendar.js +77 -0
  107. package/dist/utils/error-handling.d.ts +6 -0
  108. package/dist/utils/error-handling.d.ts.map +1 -0
  109. package/dist/utils/error-handling.js +16 -0
  110. package/dist/utils/html.d.ts +7 -0
  111. package/dist/utils/html.d.ts.map +1 -0
  112. package/dist/utils/html.js +13 -0
  113. package/dist/utils/index.d.ts +13 -0
  114. package/dist/utils/index.d.ts.map +1 -0
  115. package/dist/utils/index.js +12 -0
  116. package/dist/utils/locale.d.ts +68 -0
  117. package/dist/utils/locale.d.ts.map +1 -0
  118. package/dist/utils/locale.js +318 -0
  119. package/dist/utils/metadata.d.ts +39 -0
  120. package/dist/utils/metadata.d.ts.map +1 -0
  121. package/dist/utils/metadata.js +66 -0
  122. package/dist/utils/oauth.d.ts +16 -0
  123. package/dist/utils/oauth.d.ts.map +1 -0
  124. package/dist/utils/oauth.js +42 -0
  125. package/dist/utils/screen.d.ts +26 -0
  126. package/dist/utils/screen.d.ts.map +1 -0
  127. package/dist/utils/screen.js +44 -0
  128. package/dist/utils/settings.d.ts +38 -0
  129. package/dist/utils/settings.d.ts.map +1 -0
  130. package/dist/utils/settings.js +89 -0
  131. package/dist/utils/template.d.ts +2 -0
  132. package/dist/utils/template.d.ts.map +1 -0
  133. package/dist/utils/template.js +6 -0
  134. package/dist/utils/theme.d.ts +47 -0
  135. package/dist/utils/theme.d.ts.map +1 -0
  136. package/dist/utils/theme.js +183 -0
  137. package/dist/utils/utm.d.ts +23 -0
  138. package/dist/utils/utm.d.ts.map +1 -0
  139. package/dist/utils/utm.js +30 -0
  140. package/dist/utils/weather.d.ts +84 -0
  141. package/dist/utils/weather.d.ts.map +1 -0
  142. package/dist/utils/weather.js +272 -0
  143. package/package.json +2 -1
@@ -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"}