PageSourceSearch

https://pumasafety.com.au/assets/template_editor/richtext/dialog-XbcthfX.js

js pumasafety.com.au collected 2026-09-24 12:50:21 UTC 5,535 bytes, 223 lines download raw bytes

1import { LitElement, html, css } from 'lit';
2
3export class RichtextDialog extends LitElement {
4    static properties = {
5        open: { type: Boolean, reflect: true },
6        value: { type: Object }
7    };
8
9    // noinspection CssUnusedSymbol
10    static styles = css`
11:host {
12    display: inline-block;
13    position: relative;
14    cursor: pointer;
15}
16.btn {
17    width: 1.5rem;
18    height: 1rem;
19    display: flex;
20    align-items: center;
21    justify-items: center;
22    padding: .25rem;
23    border: 1px solid transparent;
24    border-radius: .25rem;
25    background: transparent;
26}
27.btn:after {
28    content: '›';
29    transform: rotate(90deg);
30    margin-left: .25rem;
31}
32.btn:hover {
33    border: 1px solid #d0d0d0;
34}
35.btn:active, :host([active]) .btn {
36    border: 1px solid #d0d0d0;
37    background-color: #d0d0d0;
38}
39
40.dialog {
41    position: absolute;
42    top: 1.75rem;
43    left: 0;
44    background: #fff;
45    border: 1px solid #d0d0d0;
46    border-radius: .25rem;
47    padding: .5rem;
48    z-index: 10000;
49    display: none;
50    min-width: 200px;
51    box-shadow: 0 4px 8px rgba(0,0,0,0.08);
52}
53.dialog.open {
54    display: block;
55}
56.row {
57    display: flex;
58    gap: .5rem;
59    align-items: center;
60}
61input[type="text"] {
62    flex: 1;
63    padding: .25rem .4rem;
64    border: 1px solid #ddd;
65    border-radius: .25rem;
66}
67button {
68    font-size: .6875rem;
69}
70button.ok {
71    padding: .25rem .6rem;
72    border-radius: .25rem;
73    border: 1px solid #d0d0d0;
74    background: #fff;
75    cursor: pointer;
76}
77button.ok:hover {
78    background: #e9e9e9;
79}
80.actions {
81    margin-top: .5rem;
82    display:flex;
83    justify-content:flex-end;
84    gap:.5rem;
85}
86button.cancel {
87    padding:.2rem .5rem;
88    border-radius:.25rem;
89    border:1px solid transparent;
90    background:transparent;
91    cursor:pointer;
92}
93`;
94
95    constructor() {
96        super();
97        this.open = false;
98        this.value = {};
99        this._onDocumentClick = this._onDocumentClick.bind(this);
100    }
101
102    render() {
103        const open = this.open ? 'open' : '';
104        return html`
105<a class="btn" @mousedown="${this._onMouseDown}" @click="${this._toggle}">
106    <slot></slot>
107</a>
108<div class="dialog ${open}" role="dialog" @mousedown="${this._onMouseDown}" @click="${this._intercept}" aria-hidden="${!this.open}">
109    <div class="row">
110        <slot name="fields"></slot>
111    </div>
112    <div class="actions">
113        <button class="cancel" @click="${this._onCancel}" type="button">Cancel</button>
114        <button class="ok" @click="${this._onOk}" type="button">OK</button>
115    </div>
116</div>
117`;
118    }
119
120    _intercept(evt) {
121        evt.stopPropagation();
122        evt.preventDefault();
123    }
124
125    firstUpdated() {
126        // Ensure clicks outside close the dialog
127        document.addEventListener('click', this._onDocumentClick);
128    }
129
130    disconnectedCallback() {
131        super.disconnectedCallback();
132        document.removeEventListener('click', this._onDocumentClick);
133    }
134
135    _onMouseDown(evt) {
136        // prevent editor losing focus when used inside a toolbar
137        if (evt.target.nodeName !== 'INPUT') {
138            evt.preventDefault();
139        }
140    }
141
142    _toggle(evt) {
143        evt.preventDefault();
144        evt.stopPropagation();
145        this.open = !this.open;
146        if (this.open) {
147            this._saveSelection();
148            // focus input next tick
149            requestAnimationFrame(() => {
150                const inp = this.renderRoot.querySelector('#href');
151                inp?.focus();
152                inp?.select();
153            });
154        }
155    }
156
157    _saveSelection() {
158        try {
159            const sel = document.getSelection();
160            if (!sel || sel.rangeCount === 0) {
161                this._savedSelection = null;
162                return;
163            }
164            // Clone ranges because Range objects may be mutated later.
165            const ranges = [];
166            for (let i = 0; i < sel.rangeCount; i++) {
167                ranges.push(sel.getRangeAt(i).cloneRange());
168            }
169            this._savedSelection = ranges;
170        } catch (err) {
171            // If any error occurs, don't block user — clear saved selection.
172            this._savedSelection = null;
173        }
174    }
175
176    _restoreSelection() {
177        if (!this._savedSelection) return;
178        try {
179            const sel = document.getSelection();
180            if (!sel) return;
181            sel.removeAllRanges();
182            for (const r of this._savedSelection) {
183                sel.addRange(r);
184            }
185        } catch (err) {
186            // ignore restore failures
187        } finally {
188            this._savedSelection = null;
189        }
190    }
191
192    _onCancel(e) {
193        e.preventDefault();
194        this._restoreSelection();
195        this.open = false;
196    }
197
198    _onOk(e) {
199        e.preventDefault();
200        // Emit a custom event with the href value
201        this._restoreSelection();
202        const value = {};
203        this.querySelectorAll('[data-name]').forEach(input => {
204            value[input.getAttribute('data-name')] = input.value;
205        });
206        this.dispatchEvent(new CustomEvent('rt-state', {
207            detail: { name: this.getAttribute('name'), value},
208            bubbles: true,
209            composed: true
210        }));
211        this.open = false;
212    }
213
214    _onDocumentClick(e) {
215        // Close if clicking outside component
216        if (!this.contains(e.target) && this.open) {
217            this._restoreSelection();
218            this.open = false;
219        }
220    }
221}
222
223customElements.define('mypip-richtext-dialog', RichtextDialog);

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.