PageSourceSearch

https://www.eventeny.com/js/Layouts/SectionTemplates/EventProgrammingScheduleTemplate.js?v=1790189618

js eventeny.com collected 2026-09-24 08:56:03 UTC 8,520 bytes, 260 lines download raw bytes

1import { BaseTemplate } from './BaseTemplate.js';
2
3/**
4 * <event-programming-schedule-template> — section layout for the event Schedule page.
5 *
6 * Mirrors the slot/responsive pattern of ApplicationListTemplate / ListPageTemplate
7 * but with extra rows for the search bar and the action toolbar (live toggle, add session,
8 * access-type / view-toggle dropdowns, share, print, options menu) and a conflict banner.
9 *
10 * Slots:
11 *   banner          — promo / contextual banner above the header
12 *   header-title    — <h1>
13 *   watch-tutorial  — tutorial link
14 *   search          — search input
15 *   filter          — filter button
16 *   live-toggle     — Draft/Live toggle
17 *   add-session     — primary "Add session" button
18 *   access-type     — public/private link-dropdown
19 *   view-toggle     — list/day link-dropdown
20 *   share-embed     — share icon
21 *   print           — print icon
22 *   options         — overflow ellipsis menu
23 *   conflict-banner — conflict notification banner
24 *   (default)       — page content (calendar / list)
25 *
26 * The schedule page wraps its content in #body_wrapp / #page-contents which already
27 * provides the side-nav offset, so the BaseTemplate 300px host margin is overridden.
28 */
29
30class EventProgrammingScheduleTemplate extends BaseTemplate {
31  constructor() {
32    super();
33  }
34
35  connectedCallback() {
36    super.connectedCallback();
37
38    this.updateResponsiveStyles();
39    this.render();
40
41    this._handleResizeBound = () => {
42      this.updateResponsiveStyles();
43    };
44    window.addEventListener('resize', this._handleResizeBound);
45  }
46
47  disconnectedCallback() {
48    window.removeEventListener('resize', this._handleResizeBound);
49    super.disconnectedCallback();
50  }
51
52  // The schedule page's #body_wrapp already handles side-nav offset, so neutralize
53  // BaseTemplate's 300px host margin here.
54  updateLeftPosition() {
55    this.style.setProperty('margin-left', '0', 'important');
56  }
57
58  render() {
59    const slotWrapper = this.getContentWrapper();
60    if (!slotWrapper) return;
61
62    slotWrapper.innerHTML = `
63      <style>
64        :host {
65          display: block;
66          padding: 16px !important;
67          box-sizing: border-box;
68        }
69
70        /* Replaces the legacy .schedule-manage-action-container hide/show — toggle the
71           hide-toolbar attribute on the host instead of class-toggling the wrapper. */
72        :host([hide-toolbar]) .header-search-actions,
73        :host([hide-toolbar]) .tools-row {
74          display: none;
75        }
76
77        .template-header {
78          display: flex;
79          flex-direction: column;
80        }
81
82        .header-top {
83          display: flex;
84          justify-content: space-between;
85          align-items: center;
86          flex-wrap: wrap;
87          margin-bottom: 0.5rem;
88        }
89
90        /* Desktop: search/filter on the left, action buttons on the right — single row.
91           Mobile: stacks vertically, action buttons go full-width. */
92        .header-search-actions {
93          display: flex;
94          align-items: center;
95          gap: 0.75rem;
96          margin-bottom: 0.75rem;
97        }
98        /* Section must actually grow — without flex-grow, search-bar-component's
99           width:100% resolves against the parent's content size and the input collapses. */
100        .search-section {
101          flex: 1 1 auto;
102          max-width: 800px;
103          display: flex;
104          align-items: center;
105          gap: 0.5rem;
106          min-width: 0;
107        }
108        /* All buttons in the action cluster sit on the right on desktop. */
109        .actions-section {
110          display: flex;
111          justify-content: flex-end;
112          align-items: center;
113          gap: 0.5rem;
114          flex: 0 0 auto;
115          margin-left: auto;
116        }
117        /* share-embed + options pair — sits at natural width on desktop, fills the row on mobile. */
118        .share-embed-options {
119          display: flex;
120          align-items: center;
121          gap: 0.5rem;
122        }
123        ::slotted([slot="search"]) { flex: 1 1 auto; min-width: 0; max-width: 740px; }
124        ::slotted([slot="filter"]) { flex: 0 0 auto; }
125        ::slotted([slot="print"]) {
126          display: flex !important;
127          align-items: center;
128          justify-content: center;
129          align-self: center;
130          width: 32px;
131          height: 32px;
132          font-size: 20px;
133          border: 1px solid #000 !important;
134          border-radius: 4px;
135          box-sizing: border-box;
136        }
137
138        /* Utility row sitting between the conflict banner and the calendar content:
139           live-toggle + access-type / view-toggle dropdowns + print, right-aligned. */
140        .tools-row {
141          display: flex;
142          justify-content: flex-end;
143          align-items: center;
144          gap: 0.5rem;
145          flex-wrap: wrap;
146          padding: 0.75rem 0;
147        }
148
149        ::slotted(h1[slot="header-title"]) {
150          color: #038580;
151          font-size: 32px;
152          font-weight: 500;
153          line-height: 40px;
154          margin: 0;
155        }
156
157        ::slotted(a[slot="watch-tutorial"]) {
158          display: inline-flex;
159          align-items: center;
160          font-size: 14px;
161          color: #026E80 !important;
162          text-decoration: none;
163          font-weight: 500;
164          gap: 4px;
165          white-space: nowrap;
166          line-height: 18px;
167        }
168
169        .template-content { padding: 0; }
170
171        @media (max-width: 768px) {
172          .header-top {
173            flex-direction: row;
174            justify-content: space-between;
175            gap: 0.5rem;
176          }
177
178          /* Stack search above actions. Within actions, +New session takes its own
179             full-width row; Share & embed + options share the next row. */
180          .header-search-actions {
181            flex-direction: column;
182            align-items: stretch;
183          }
184          .actions-section {
185            flex-wrap: wrap;
186            width: 100%;
187            margin-left: 0;
188            justify-content: flex-start;
189          }
190          .actions-section ::slotted([slot="add-session"]) {
191            flex: 1 1 100%;
192          }
193          .share-embed-options {
194            flex: 1 1 100%;
195            width: 100%;
196          }
197          .share-embed-options ::slotted([slot="share-embed"]) {
198            flex: 1 1 auto;
199          }
200          .share-embed-options ::slotted([slot="options"]) {
201            flex: 0 0 auto;
202          }
203        }
204      </style>
205
206      <slot name="banner"></slot>
207      <header class="template-header">
208        <div class="header-top">
209          <slot name="header-title"></slot>
210          <slot name="watch-tutorial"></slot>
211        </div>
212        <div class="header-search-actions">
213          <div class="search-section">
214            <slot name="search"></slot>
215            <slot name="filter"></slot>
216          </div>
217          <div class="actions-section">
218            <slot name="add-session"></slot>
219            <div class="share-embed-options">
220              <slot name="share-embed"></slot>
221              <slot name="options"></slot>
222            </div>
223          </div>
224        </div>
225      </header>
226      <slot name="conflict-banner"></slot>
227      <div class="tools-row">
228        <slot name="live-toggle"></slot>
229        <slot name="access-type"></slot>
230        <slot name="view-toggle"></slot>
231        <slot name="print"></slot>
232      </div>
233      <div class="template-content">
234        <slot></slot>
235      </div>
236    `;
237
238    // Default content for an empty watch-tutorial link slot
239    requestAnimationFrame(() => {
240      const slottedLink = this.querySelector('a[slot="watch-tutorial"]');
241      if (slottedLink && slottedLink.innerHTML.trim() === '') {
242        slottedLink.innerHTML = `<i class="uil uil-play-circle size-16"></i> Watch tutorial`;
243      }
244    });
245  }
246
247  updateResponsiveStyles() {
248    // Mobile: +New session and Share & embed each take full-width on their own row.
249    // Desktop: both sit inline next to the search bar at their natural widths.
250    const isMobile = window.innerWidth <= 768;
251    ['add-session', 'share-embed'].forEach(slotName => {
252      const button = this.querySelector(`button-element[slot="${slotName}"], basic-button-element[slot="${slotName}"]`);
253      if (!button) return;
254      if (isMobile) button.setAttribute('full-width', '');
255      else button.removeAttribute('full-width');
256    });
257  }
258}
259
260customElements.define('event-programming-schedule-template', EventProgrammingScheduleTemplate);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.