PageSourceSearch

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

js eventeny.com collected 2026-09-24 08:55:58 UTC 9,977 bytes, 298 lines download raw bytes

1import { BaseTemplate } from './BaseTemplate.js';
2
3/**
4 * This file defines the <application-list-template> web component, which extends a base layout
5 * (BaseTemplate) to render a structured UI template for application listing views. It provides:
6 *
7 * - A customizable header area with slots for breadcrumb/tabs, title, tutorial link, and action buttons.
8 * - A main content slot for injecting dynamic or templated content.
9 * - Responsive layout behavior for both desktop and mobile breakpoints.
10 * - Optional full-width button behavior on mobile (≤ 768px).
11 * - Dynamic rendering of a default label for the tutorial link slot.
12 * - Custom event dispatching for click actions on slotted buttons:
13 *     - 'primary-btn-clicked' for create-application
14 *     - 'primary-btn-2-clicked' for create-template
15 *     - 'secondary-btn-clicked' for share-embed
16 *
17 * Usage:
18 *  <application-list-template>
19 *    <breadcrumb-tabs-component slot="tabs" tabs-config="..."></breadcrumb-tabs-component>
20 *    <h1 slot="header-title">Title</h1>
21 *    <a slot="watch-tutorial" href="#">Watch</a>
22 *    <button-element slot="create-application" id="app-btn">Create App</button-element>
23 *    <button-element slot="create-template" id="template-btn">Create Template</button-element>
24 *    <button-element slot="share-embed" id="share-btn">Share</button-element>
25 *    <div>Your list or content here</div>
26 *  </application-list-template>
27 *
28 * Dependencies:
29 * - Extends: BaseTemplate (imported from './BaseTemplate.js')
30 * - Expects: <button-element> components with slots matching action names.
31 *
32 * @slot tabs - Optional breadcrumb/tabs row (e.g. <breadcrumb-tabs-component>) rendered above the
33 *              title row, mirroring the same slot on the sibling ApplicationDetailsTemplate. Renders
34 *              nothing when left unslotted, so existing consumers of this template are unaffected.
35 * @slot breadcrumb - Optional breadcrumb nav rendered above the title, inside the left header column.
36 * @slot section-heading - Optional secondary heading rendered below the title.
37 * @slot description - Optional supporting copy rendered below the section heading.
38 *                     These three collapse when unslotted, so they add no vertical gap.
39 *
40 * @cssprop --template-header-padding - Padding of the header row (tabs/title/actions). Default: 16px 32px.
41 * @cssprop --template-content-padding - Padding of the default-slot content area. Default: 8px 32px 16px.
42 *          Set either via the `style` attribute on the host element (e.g.
43 *          `<application-list-template style="--template-header-padding: 1rem 1rem .5rem;">`),
44 *          same pattern as BaseTemplate's own `--margin-left`.
45 */
46
47// changes to my template here
48class ApplicationListTemplate extends BaseTemplate {
49  constructor() {
50    super();
51  }
52
53  connectedCallback() {
54    super.connectedCallback();
55
56    // Responsive styling on load handler
57    this.updateLeftPosition();
58    this.updateResponsiveStyles(); 
59    this.render();
60
61    // Responsive styling on resize handler
62    this._handleResizeBound = () => {
63      this.updateLeftPosition();
64      this.updateResponsiveStyles();
65    };
66
67    window.addEventListener('resize', this._handleResizeBound);
68  }
69
70  disconnectedCallback() {
71    window.removeEventListener('resize', this._handleResizeBound);
72    super.disconnectedCallback();
73  }
74
75  render() {
76    const slotWrapper = this.getContentWrapper();
77    if(!slotWrapper) return;
78
79    slotWrapper.innerHTML = `
80      <style>
81        .template-header {
82          display: flex;
83          flex-direction: column;
84          padding: var(--template-header-padding, 16px 32px);
85        }
86
87        /* Toggled in render() -- an unslotted <slot> is still a DOM node, so :empty can't detect it */
88        .header-tabs.has-content {
89          margin-bottom: 8px;
90        }
91
92        /* Two-column page header: content (left) + help/actions (right) */
93        .header-row {
94          display: flex;
95          align-items: flex-start;
96          justify-content: space-between;
97          gap: 40px;
98          flex-wrap: wrap;
99        }
100
101        .header-main {
102          display: flex;
103          flex-direction: column;
104          gap: 8px;
105          flex: 1 0 0;
106          min-width: 0;
107        }
108
109        .header-aside {
110          display: flex;
111          flex-direction: column;
112          align-items: flex-end;
113          justify-content: flex-start;
114          gap: 16px;
115        }
116
117        .header-actions {
118          display: flex;
119          justify-content: flex-end;
120          gap: 8px;
121          flex-wrap: wrap;
122        }
123
124        /* An optional slot with no assigned content is collapsed 
124so it adds no gap */
125        slot.is-empty { display: none; }
126
127        ::slotted([slot="breadcrumb"]) {
128          display: flex;
129          align-items: center;
130          gap: 4px;
131          font-size: 14px;
132          line-height: 18px;
133          margin: 0;
134        }
135
136        ::slotted(h1[slot="header-title"]) {
137          color: #038580;
138          font-size: 32px;
139          font-weight: 500;
140          line-height: 40px;
141          margin: 0;
142        }
143
144        ::slotted([slot="section-heading"]) {
145          color: #222222;
146          font-size: 24px;
147          font-weight: 600;
148          line-height: 32px;
149          margin: 0;
150        }
151
152        ::slotted([slot="description"]) {
153          color: #5E6463;
154          font-size: 16px;
155          font-weight: 400;
156          line-height: 24px;
157          margin: 0;
158        }
159
160        ::slotted(a[slot="watch-tutorial"]) {
161          display: inline-flex;
162          align-items: center;
163          font-size: 14px;
164          color: #026E80 !important;
165          text-decoration: none;
166          font-weight: 500;
167          gap: 4px;
168          white-space: nowrap;
169          line-height: 18px;
170        }
171
172        .template-content {
173          padding: var(--template-content-padding, 8px 32px 16px);
174        }
175
176        @media (max-width: 768px) {
177          .template-header { padding: 16px; }
178          .header-row {
179            flex-direction: column;
180            gap: 16px;
181          }
182          .header-aside {
183            align-items: stretch;
184            width: 100%;
185          }
186          .header-actions {
187            flex-direction: column;
188            width: 100%;
189          }
190          .header-actions ::slotted(*) {
191            width: 100%;
192          }
193          .template-content { padding: 8px 16px 16px; }
194        }
195      </style>
196
197      <slot name="banner"></slot>
198      <header class="template-header">
199        <div class="header-tabs">
200          <slot name="tabs"></slot>
201        </div>
202        <div class="header-row">
203          <div class="header-main">
204            <slot name="breadcrumb"></slot>
205            <slot name="header-title"></slot>
206            <slot name="section-heading"></slot>
207            <slot name="description"></slot>
208          </div>
209          <div class="header-aside">
210            <slot name="watch-tutorial"></slot>
211            <div class="header-actions">
212              <slot name="header-actions"></slot>
213            </div>
214          </div>
215        </div>
216      </header>
217      <div class="template-content">
218        <slot></slot>
219      </div>
220    `;
221
222    // Inject default inner content into the watch link if it's empty
223    requestAnimationFrame(() => {
224      const slottedLink = this.querySelector('a[slot="watch-tutorial"]');
225      if(slottedLink && slottedLink.innerHTML.trim() === '') {
226        slottedLink.innerHTML = `<i class="uil uil-play-circle size-16"></i> Watch tutorial videos`;
227      }
228    });
229
230    // Collapse optional header slots that have no assigned content so they add no vertical gap
231    ['breadcrumb', 'section-heading', 'description', 'watch-tutorial'].forEach((name) => {
232      const slot = slotWrapper.querySelector(`slot[name="${name}"]`);
233      if(!slot) return;
234      const syncEmpty = () => {
235        const hasContent = slot.assignedNodes({ flatten: true }).some(
236          (n) => n.nodeType === 1 || (n.nodeType === 3 && n.textContent.trim() !== '')
237        );
238        slot.classList.toggle('is-empty', !hasContent);
239      };
240      slot.addEventListener('slotchange', syncEmpty);
241      syncEmpty();
242    });
243
244    // Only reserve spacing for the tabs row when something is actually slotted into it.
245    const tabsSlot = slotWrapper.querySelector('slot[name="tabs"]');
246    const tabsWrapper = slotWrapper.querySelector('.header-tabs');
247    if(tabsSlot && tabsWrapper) {
248      const updateTabsVisibility = () => {
249        tabsWrapper.classList.toggle('has-content', tabsSlot.assignedElements({ flatten: true }).length > 0);
250      };
251      tabsSlot.addEventListener('slotchange', updateTabsVisibility);
252      updateTabsVisibility();
253    }
254
255    this.setupEventHandlers();
256  }
257
258  updateResponsiveStyles() {
259  //matchMedia keeps this in step with the shadow stylesheet's own 768px query; innerWidth counts the scrollbar, the media query does not
260  const isMobile = window.matchMedia('(max-width: 768px)').matches;
261  const buttons = this.querySelectorAll(`button-element[slot="header-actions"]`);
262  
263    buttons.forEach(button => {
264      
265      if(button) {
266        if(isMobile) {
267          button.setAttribute('full-width', '');
268        } else {
269          button.removeAttribute('full-width');
270        }
271      }
272    });
273  }
274
275
276  setupEventHandlers() {
277    requestAnimationFrame(() => {
278      const shadow = this.shadowRoot;
279
280      const buttons = this.querySelectorAll(`button-element[slot="header-actions"]`);
281
282      buttons.forEach((button) => {
283          if(button.hasAttribute('dropdown')) return;
284          button.addEventListener('click', () => {
285            const buttonId = button.id || null;
286            this.dispatchEvent(new CustomEvent('click', {
287              detail: { id: buttonId },
288              bubbles: true,
289              composed: true
290            }));
291          });
292        
293      });
294    });
295  }
296}
297
298customElements.define('application-list-template', ApplicationListTemplate);

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.