PageSourceSearch

https://www.levata.com/static/version1791201136/frontend/Logiscent…/en_US/Logiscenter_Base/js/ui-components.js

js levata.com collected 2026-10-06 15:08:23 UTC 5,640 bytes, 175 lines download raw bytes

1'use strict';
2
3document.addEventListener('alpine:init', () => {
4    /**
5     * Action Dropdown Component
6     *
7     * A reusable dropdown menu component for table actions (Edit, Delete, etc.)
8     * Follows Hyvä Themes and Alpine.js 3 best practices.
9     *
10     * @example Basic usage:
11     * <td x-data="actionDropdown()">
12     *     <template x-ref="triggerTemplate">
13     *         <button x-bind="trigger" class="text-gray-400 hover:text-gray-600">
14     *             <!-- Icon here -->
15     *         </button>
16     *     </template>
17     *     <template x-ref="menuTemplate">
18     *         <a href="#" @click="close()" class="dropdown-item">Edit</a>
19     *         <a href="#" @click="close()" class="dropdown-item">Delete</a>
20     *     </template>
21     * </td>
22     *
23     * @example With custom actions:
24     * <div x-data="actionDropdown({ onOpen: () => console.log('opened') })">
25     *     ...
26     * </div>
27     */
28    Alpine.data('actionDropdown', (config = {}) => ({
29        isOpen: false,
30
31        init() {
32            if (config.onInit && typeof config.onInit === 'function') {
33                config.onInit.call(this);
34            }
35        },
36
37        trigger: {
38            ['@click']() {
39                this.toggle();
40            },
41            ['@click.away']() {
42                this.close();
43            },
44            ['@keydown.escape.window']() {
45                this.close();
46            },
47            [':aria-expanded']() {
48                return this.isOpen;
49            }
50        },
51
52        menu: {
53            ['x-show']() {
54                return this.isOpen;
55            },
56            ['x-transition:enter']: 'transition ease-out duration-100',
57            ['x-transition:enter-start']: 'opacity-0 scale-95',
58            ['x-transition:enter-end']: 'opacity-100 scale-100',
59            ['x-transition:leave']: 'transition ease-in duration-75',
60            ['x-transition:leave-start']: 'opacity-100 scale-100',
61            ['x-transition:leave-end']: 'opacity-0 scale-95',
62            ['style']: 'display: none;'
63        },
64
65        toggle() {
66            this.isOpen = !this.isOpen;
67            if (this.isOpen) {
68                this.$nextTick(() => {
69                    this.scrollIntoViewIfNeeded();
70                    if (config.onOpen && typeof config.onOpen === 'function') {
71                        config.onOpen.call(this);
72                    }
73                });
74            }
75        },
76
77        open() {
78            if (!this.isOpen) {
79                this.isOpen = true;
80                this.$nextTick(() => {
81                    this.scrollIntoViewIfNeeded();
82                    if (config.onOpen && typeof config.onOpen === 'function') {
83                        config.onOpen.call(this);
84                    }
85                });
86            }
87        },
88
89        close() {
90            if (this.isOpen) {
91                this.isOpen = false;
92                if (config.onClose && typeof config.onClose === 'function') {
93                    config.onClose.call(this);
94                }
95            }
96        },
97
98        scrollIntoViewIfNeeded() {
99            if (this.$refs.menu) {
100                this.$refs.menu.scrollIntoView({
101                    behavior: 'smooth',
102                    block: 'nearest'
103                });
104            }
105        }
106    }));
107
108    /**
109     * Table Action Menu Component
110     *
111     * Simplified version specifically for table row actions.
112     * Pre-configured with common defaults for table dropdowns.
113     *
114     * @example
115     * <td class="relative" x-data="tableActionMenu()">
116     *     <button x-bind="trigger">...</button>
117     *     <div x-ref="menu" x-bind="menu" class="absolute right-0 mt-2 ...">
118     *         <button @click="action('edit', itemData)">Edit</button>
119     *         <button @click="action('delete', itemData)">Delete</button>
120     *     </div>
121     * </td>
122     */
123    Alpine.data('tableActionMenu', (config = {}) => ({
124        isOpen: false,
125        itemData: config.itemData || null,
126
127        trigger: {
128            ['@click']() {
129                this.isOpen = !this.isOpen;
130                if (this.isOpen) {
131                    this.$nextTick(() => {
132                        if (this.$refs.menu) {
133                            this.$refs.menu.scrollIntoView({
134                                behavior: 'smooth',
135                                block: 'nearest'
136                            });
137                        }
138                    });
139                }
140            },
141            ['@click.away']() {
142                this.isOpen = false;
143            },
144            ['@keydown.escape.window']() {
145                this.isOpen = false;
146            }
147        },
148
149        menu: {
150            ['x-show']() {
151                return this.isOpen;
152            },
153            ['x-transition:enter']: 'transition ease-out duration-100',
154            ['x-transition:enter-start']: 'opacity-0 scale-95',
155            ['x-transition:enter-end']: 'opacity-100 scale-100',
156            ['x-transition:leave']: 'transition ease-in duration-75',
157            ['x-transition:leave-start']: 'opacity-100 scale-100',
158            ['x-transition:leave-end']: 'opacity-0 scale-95',
159            ['style']: 'display: none;'
160        },
161
162        action(actionType, data) {
163            this.isOpen = false;
164            const eventName = config.eventPrefix
165                ? `${config.eventPrefix}-${actionType}`
166                : `open-${actionType}-modal`;
167
168            this.$dispatch(eventName, { [config.dataKey || 'data']: data || this.itemData });
169        },
170
171        close() {
172            this.isOpen = false;
173        }
174    }));
175});

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.