bootstrap-email-wysiwyg 0.1.0

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Süleyman Kenar
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,374 @@
1
+ # bootstrap-email-wysiwyg
2
+
3
+ A [Lexical](https://lexical.dev)-based WYSIWYG editor for authoring
4
+ [Bootstrap Email](https://bootstrap-email.com) templates. You edit visually; it
5
+ outputs clean, class-based Bootstrap Email source HTML that you feed to the
6
+ Bootstrap Email compiler to produce bullet-proof, cross-client email markup.
7
+
8
+ - 🧱 **Bootstrap Email native** — every block is a `<div>`; colors, sizes,
9
+ spacing, buttons, images and rules all emit real Bootstrap Email classes
10
+ (`text-center`, `bg-blue-500`, `w-64`, `mt-5`, `btn btn-primary`, …).
11
+ - 🎨 **Full palette** — text / background / border color pickers over the
12
+ complete Bootstrap Email color scale, plus custom colors.
13
+ - 🔤 **Type scale** — increase/decrease font size across `text-xs … text-7xl`.
14
+ - 🖼️ **Images & separators** — insert from URL with an inline settings gear
15
+ (fluid / fixed / max-width sizing; configurable rule spacing).
16
+ - 🔘 **Buttons** — Bootstrap Email buttons with independent text/bg/border color
17
+ and size; remembers your last styling.
18
+ - 🔌 **Controlled or headless** — `onChange`, `initialContent`, an imperative
19
+ `ref`, and framework-free command functions for building your own UI.
20
+ - 📦 **Typed** — ships TypeScript declarations.
21
+
22
+ > **Status:** pre-1.0 (`0.0.0`). The API is usable and covered by tests, but may
23
+ > still change before a stable release.
24
+
25
+ ---
26
+
27
+ ## Table of contents
28
+
29
+ - [Installation](#installation)
30
+ - [Quick start](#quick-start)
31
+ - [Styling](#styling)
32
+ - [Getting content out](#getting-content-out)
33
+ - [Seeding content](#seeding-content)
34
+ - [Exporting HTML](#exporting-html)
35
+ - [Compiling to email HTML](#compiling-to-email-html)
36
+ - [Component API](#component-api)
37
+ - [Imperative handle (ref)](#imperative-handle-ref)
38
+ - [Toolbar features](#toolbar-features)
39
+ - [Headless / custom toolbar](#headless--custom-toolbar)
40
+ - [Programmatic commands](#programmatic-commands)
41
+ - [How it works](#how-it-works)
42
+ - [Framework support](#framework-support)
43
+ - [Development](#development)
44
+ - [Roadmap](#roadmap)
45
+ - [License](#license)
46
+
47
+ ---
48
+
49
+ ## Installation
50
+
51
+ ```sh
52
+ npm install bootstrap-email-wysiwyg
53
+ ```
54
+
55
+ `react` and `react-dom` (>= 18) are **peer dependencies** — install them in your
56
+ app if you haven't already:
57
+
58
+ ```sh
59
+ npm install react react-dom
60
+ ```
61
+
62
+ ## Quick start
63
+
64
+ ```tsx
65
+ import { BootstrapEmailEditor } from "bootstrap-email-wysiwyg";
66
+ import "bootstrap-email-wysiwyg/styles.css";
67
+
68
+ export function MyEditor() {
69
+ return <BootstrapEmailEditor placeholder="Compose your email…" />;
70
+ }
71
+ ```
72
+
73
+ That renders the editor with the built-in toolbar. To do anything with the
74
+ content, read [Getting content out](#getting-content-out).
75
+
76
+ ## Styling
77
+
78
+ The editor ships a single stylesheet for its chrome (toolbar, pickers, image /
79
+ rule overlays) and a light in-editor preview of the Bootstrap Email classes.
80
+ **Import it once** in your app:
81
+
82
+ ```ts
83
+ import "bootstrap-email-wysiwyg/styles.css";
84
+ ```
85
+
86
+ > The stylesheet only styles the **editing surface**. Your final email is styled
87
+ > by the Bootstrap Email compiler, not by this CSS.
88
+
89
+ ## Getting content out
90
+
91
+ The editor is uncontrolled by default. Get its content two ways:
92
+
93
+ ### `onChange` (fires on every edit)
94
+
95
+ ```tsx
96
+ import { BootstrapEmailEditor, type EditorChange } from "bootstrap-email-wysiwyg";
97
+
98
+ function Editor() {
99
+ const handleChange = ({ html, json }: EditorChange) => {
100
+ // `html` — Bootstrap Email source (content fragment)
101
+ // `json` — serialized editor state, for persistence / initialContent
102
+ console.log(html);
103
+ };
104
+
105
+ return <BootstrapEmailEditor onChange={handleChange} />;
106
+ }
107
+ ```
108
+
109
+ ### Imperative `ref` (on demand)
110
+
111
+ ```tsx
112
+ import { useRef } from "react";
113
+ import {
114
+ BootstrapEmailEditor,
115
+ type BootstrapEmailEditorHandle,
116
+ } from "bootstrap-email-wysiwyg";
117
+
118
+ function Editor() {
119
+ const ref = useRef<BootstrapEmailEditorHandle>(null);
120
+
121
+ const save = () => {
122
+ const html = ref.current?.getHtml(); // fragment
123
+ const doc = ref.current?.getHtml({ document: true }); // full document
124
+ const state = ref.current?.getJson(); // for reloading later
125
+ // …persist state / send html…
126
+ };
127
+
128
+ return (
129
+ <>
130
+ <BootstrapEmailEditor ref={ref} />
131
+ <button onClick={save}>Save</button>
132
+ </>
133
+ );
134
+ }
135
+ ```
136
+
137
+ ## Seeding content
138
+
139
+ Pass a previously saved state (from `getJson()` or `onChange().json`) as
140
+ `initialContent`. This is applied once, at mount (default-value semantics — it is
141
+ **not** a controlled `value`).
142
+
143
+ ```tsx
144
+ <BootstrapEmailEditor initialContent={savedJson} />
145
+ ```
146
+
147
+ > `initialContent` accepts the **serialized editor-state JSON**, not raw HTML.
148
+ > JSON round-trips losslessly; HTML seeding is on the roadmap.
149
+
150
+ ## Exporting HTML
151
+
152
+ Use the component `ref`/`onChange`, or call the exporter directly with a Lexical
153
+ editor instance:
154
+
155
+ ```ts
156
+ import { toBootstrapEmailHtml } from "bootstrap-email-wysiwyg";
157
+
158
+ toBootstrapEmailHtml(editor); // content fragment (default)
159
+ toBootstrapEmailHtml(editor, { document: true }); // full <!DOCTYPE html> document
160
+ toBootstrapEmailHtml(editor, { pretty: false }); // compact, single line
161
+ ```
162
+
163
+ **Fragment** output (default):
164
+
165
+ ```html
166
+ <div class="text-center text-blue-500">Hello world</div>
167
+ <div class="ax-center"><a href="#" class="btn btn-primary">Shop now</a></div>
168
+ <img src="https://…/logo.png" alt="Logo" class="img-fluid">
169
+ <hr class="mt-5 mb-5">
170
+ ```
171
+
172
+ **Document** output (`{ document: true }`) wraps the fragment in a minimal HTML
173
+ email document with a `.container`, ready for the compiler.
174
+
175
+ | Option | Type | Default | Description |
176
+ | ---------- | --------- | ------- | ------------------------------------------------------ |
177
+ | `document` | `boolean` | `false` | Wrap the content in a full HTML email document. |
178
+ | `pretty` | `boolean` | `true` | Pretty-print with indentation (`false` = single line). |
179
+
180
+ > `toBootstrapEmailHtml` and `cleanBootstrapHtml` use the DOM and are
181
+ > **browser-only** (call them client-side, not during SSR).
182
+
183
+ ## Compiling to email HTML
184
+
185
+ This editor produces **Bootstrap Email source** — semantic HTML with Bootstrap
186
+ Email utility classes. To turn it into final, table-based, cross-client email
187
+ HTML, run the output through the [Bootstrap Email](https://bootstrap-email.com)
188
+ compiler (the Ruby gem or a compatible port):
189
+
190
+ ```
191
+ editor → toBootstrapEmailHtml({ document: true }) → bootstrap-email compile → send
192
+ ```
193
+
194
+ ## Component API
195
+
196
+ ```tsx
197
+ import { BootstrapEmailEditor } from "bootstrap-email-wysiwyg";
198
+ ```
199
+
200
+ | Prop | Type | Default | Description |
201
+ | ---------------- | --------------------------------- | -------------------- | ----------------------------------------------------------------------- |
202
+ | `placeholder` | `string` | `"Start writing…"` | Placeholder shown when empty. |
203
+ | `toolbar` | `boolean` | `true` | Render the built-in formatting toolbar. |
204
+ | `initialContent` | `string` | `undefined` | Serialized editor state to seed the editor (see [Seeding](#seeding-content)). |
205
+ | `onChange` | `(change: EditorChange) => void` | `undefined` | Called on every edit with `{ html, json }`. |
206
+ | `onError` | `(error: Error) => void` | `undefined` | Called when Lexical throws (defaults to rethrow). |
207
+ | `children` | `ReactNode` | `undefined` | Extra plugins/components rendered **inside** the editor context. |
208
+
209
+ ```ts
210
+ interface EditorChange {
211
+ html: string; // Bootstrap Email source (fragment)
212
+ json: string; // serialized editor state
213
+ }
214
+ ```
215
+
216
+ ## Imperative handle (ref)
217
+
218
+ Attach a `ref` of type `BootstrapEmailEditorHandle`:
219
+
220
+ | Method | Signature | Description |
221
+ | --------------------- | ----------------------------------------------- | ------------------------------------------------- |
222
+ | `getHtml(options?)` | `(options?: BootstrapEmailHtmlOptions) => string` | Export Bootstrap Email HTML (fragment or document). |
223
+ | `getJson()` | `() => string` | Serialized editor state, for `initialContent`. |
224
+ | `focus()` | `() => void` | Focus the editor. |
225
+ | `clear()` | `() => void` | Remove all content. |
226
+ | `getEditor()` | `() => LexicalEditor \| null` | The underlying Lexical editor, for advanced use. |
227
+
228
+ ## Toolbar features
229
+
230
+ | Group | Controls | Emits |
231
+ | -------------- | ------------------------------------------------------------------------ | ---------------------------------------- |
232
+ | History | Undo / redo | — |
233
+ | Font size | Decrease / increase across the type scale | `text-xs … text-7xl` |
234
+ | Inline | Bold, italic, underline, strikethrough | `<strong>` `<em>` `<u>` `<s>` |
235
+ | Alignment | Left, center, right, justify | `text-*` (or `ax-*` for buttons) |
236
+ | Colors | Text, background, border (buttons) — palette + custom | `text-*`, `bg-*`, `border-*` |
237
+ | Insert | Button, image (from URL), separator | `btn`, `img-fluid`, `<hr>` |
238
+
239
+ Colors apply to the selected text (as a span), the current block, or a focused
240
+ button, depending on the selection. Images and separators show an inline **gear**
241
+ overlay for sizing / spacing.
242
+
243
+ ## Headless / custom toolbar
244
+
245
+ Turn off the built-in toolbar and build your own. Components passed as `children`
246
+ render **inside** the editor context, so `useLexicalComposerContext()` and the
247
+ command functions work:
248
+
249
+ ```tsx
250
+ import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext";
251
+ import { BootstrapEmailEditor, insertButton } from "bootstrap-email-wysiwyg";
252
+
253
+ function MyToolbar() {
254
+ const [editor] = useLexicalComposerContext();
255
+ return (
256
+ <button onClick={() => insertButton(editor, { label: "Buy", variant: "success" })}>
257
+ Add button
258
+ </button>
259
+ );
260
+ }
261
+
262
+ <BootstrapEmailEditor toolbar={false}>
263
+ <MyToolbar />
264
+ </BootstrapEmailEditor>;
265
+ ```
266
+
267
+ The prebuilt `Toolbar` component is also exported if you want to place it
268
+ yourself.
269
+
270
+ ## Programmatic commands
271
+
272
+ All commands are framework-free and take a Lexical editor (via `ref.getEditor()`
273
+ or `useLexicalComposerContext()`):
274
+
275
+ ```ts
276
+ import {
277
+ insertButton,
278
+ insertImage,
279
+ insertHr,
280
+ applyColor,
281
+ adjustFontSize,
282
+ } from "bootstrap-email-wysiwyg";
283
+
284
+ insertButton(editor, {
285
+ label: "Shop now",
286
+ href: "https://example.com",
287
+ variant: "primary", // primary | secondary | success | danger | warning | info | light | dark
288
+ outline: false,
289
+ // optional overrides (palette token or "#hex"):
290
+ textColor: null,
291
+ bgColor: "green-500",
292
+ borderColor: null,
293
+ fontSize: null, // "xs" … "7xl"
294
+ });
295
+
296
+ insertImage(editor, {
297
+ src: "https://example.com/logo.png",
298
+ alt: "Logo",
299
+ mode: "fluid", // fluid | fixed | max
300
+ width: null, // size key, e.g. "64" (256px)
301
+ height: null,
302
+ });
303
+
304
+ insertHr(editor, { top: "5", bottom: "5" }); // margin keys (mt-5 / mb-5 = 20px)
305
+
306
+ applyColor(editor, "text", "blue-500"); // "text" | "bg" | "border"; token or "#hex" or null to clear
307
+ adjustFontSize(editor, "increase"); // "increase" | "decrease"
308
+ ```
309
+
310
+ `$`-prefixed variants (`$applyColor`, `$adjustFontSize`) run inside an existing
311
+ `editor.update()` if you're composing your own updates.
312
+
313
+ Color, size, and spacing scales are exported too (`BASE_COLORS`,
314
+ `COLOR_FAMILIES`, `FONT_SIZE_KEYS`, `SIZE_STEPS`, `MARGIN_STEPS`), along with the
315
+ conversion helpers (`tokenToClass`, `hexToToken`, `fontSizeClass`, …) — handy for
316
+ building custom pickers.
317
+
318
+ ## How it works
319
+
320
+ The editor is built on [Lexical](https://lexical.dev). Custom nodes map the
321
+ document to Bootstrap Email markup:
322
+
323
+ | Node | Renders / exports as |
324
+ | ------------------------ | ----------------------------------------------------- |
325
+ | `BootstrapParagraphNode` | `<div>` line, with alignment/color/size classes |
326
+ | `ButtonNode` | `<a class="btn btn-…">` (inline) |
327
+ | `ImageNode` | `<img class="img-fluid \| w-… \| max-w-…">` (decorator) |
328
+ | `HrNode` | `<hr class="mt-… mb-…">` (decorator) |
329
+
330
+ On export, Lexical's raw HTML is cleaned (text-wrapper spans removed) and inline
331
+ colors/sizes are converted to Bootstrap Email classes. Editor state serializes to
332
+ JSON for persistence.
333
+
334
+ ## Framework support
335
+
336
+ Currently **React-first** — the editor and toolbar are built on
337
+ `@lexical/react`. The logic layer (nodes, commands, color/size/spacing scales,
338
+ export) is already framework-free, and a framework-agnostic core with thin
339
+ per-framework wrappers is on the roadmap. If you use another framework today, the
340
+ command functions and `toBootstrapEmailHtml` are usable, but you'd wire the mount
341
+ and UI yourself.
342
+
343
+ ## Development
344
+
345
+ This repo contains the library (`src/`) and a live playground (`dev/`) that
346
+ imports it directly, so changes hot-reload.
347
+
348
+ ```sh
349
+ npm install # install dependencies
350
+ npm run dev # start the playground (editor + live source preview)
351
+ npm run build # build the publishable library into dist/
352
+ npm run typecheck
353
+ ```
354
+
355
+ Behavior is covered by headless verification scripts:
356
+
357
+ ```sh
358
+ npx tsx scripts/verify-export.mjs # export API + JSON round-trip
359
+ npx tsx scripts/verify-color.mjs # color apply + class output
360
+ # …and verify-button / align / fontsize / image / hr
361
+ ```
362
+
363
+ Only `dist/` is published; the `dev/` playground stays in the repo.
364
+
365
+ ## Roadmap
366
+
367
+ - Configurable toolbar (feature flags), `readOnly` mode
368
+ - Inline links and lists (`<ul>` / `<ol>`)
369
+ - Controlled `value` and HTML seeding / import
370
+ - Framework-agnostic core + Vue/Svelte/vanilla wrappers
371
+
372
+ ## License
373
+
374
+ [MIT](./LICENSE)
@@ -0,0 +1,18 @@
1
+ "use strict";var Jt=Object.defineProperty;var Ut=(e,o,t)=>o in e?Jt(e,o,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[o]=t;var g=(e,o,t)=>Ut(e,typeof o!="symbol"?o+"":o,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("react/jsx-runtime"),m=require("react"),qt=require("@lexical/react/LexicalComposer"),Gt=require("@lexical/react/LexicalRichTextPlugin"),Yt=require("@lexical/react/LexicalContentEditable"),Vt=require("@lexical/react/LexicalHistoryPlugin"),Xt=require("@lexical/react/LexicalErrorBoundary"),O=require("@lexical/react/LexicalComposerContext"),h=require("lexical"),Z=require("@lexical/utils"),Y=require("@lexical/selection"),Zt=require("@lexical/html"),ct={paragraph:"bew-paragraph",text:{bold:"bew-text-bold",italic:"bew-text-italic",underline:"bew-text-underline",strikethrough:"bew-text-strikethrough"}},z=[{token:"primary",hex:"#0d6efd"},{token:"secondary",hex:"#6c757d"},{token:"success",hex:"#198754"},{token:"info",hex:"#0dcaf0"},{token:"warning",hex:"#ffc107"},{token:"danger",hex:"#dc3545"},{token:"light",hex:"#f8f9fa"},{token:"dark",hex:"#212529"},{token:"black",hex:"#000000"},{token:"white",hex:"#ffffff"},{token:"transparent",hex:"transparent"}],Qt={gray:["#f8f9fa","#e9ecef","#dee2e6","#ced4da","#adb5bd","#6c757d","#495057","#343a40","#212529"],blue:["#cfe2ff","#9ec5fe","#6ea8fe","#3d8bfd","#0d6efd","#0a58ca","#084298","#052c65","#031633"],indigo:["#e0cffc","#c29ffa","#a370f7","#8540f5","#6610f2","#520dc2","#3d0a91","#290661","#140330"],purple:["#e2d9f3","#c5b3e6","#a98eda","#8c68cd","#6f42c1","#59359a","#432874","#2c1a4d","#160d27"],pink:["#f7d6e6","#efadce","#e685b5","#de5c9d","#d63384","#ab296a","#801f4f","#561435","#2b0a1a"],red:["#f8d7da","#f1aeb5","#ea868f","#e35d6a","#dc3545","#b02a37","#842029","#58151c","#2c0b0e"],orange:["#ffe5d0","#fecba1","#feb272","#fd9843","#fd7e14","#ca6510","#984c0c","#653208","#331904"],yellow:["#fff3cd","#ffe69c","#ffda6a","#ffcd39","#ffc107","#cc9a06","#997404","#664d03","#332701"],green:["#d1e7dd","#a3cfbb","#75b798","#479f76","#198754","#146c43","#0f5132","#0a3622","#051b11"],teal:["#d2f4ea","#a6e9d5","#79dfc1","#4dd4ac","#20c997","#1aa179","#13795b","#0d503c","#06281e"],cyan:["#cff4fc","#9eeaf9","#6edff6","#3dd5f3","#0dcaf0","#0aa2c0","#087990","#055160","#032830"]},H=Object.entries(Qt).map(([e,o])=>({name:e,shades:o.map((t,r)=>{const s=(r+1)*100;return{shade:s,token:`${e}-${s}`,hex:t}})})),Q=new Map;for(const e of z)Q.set(e.token,e.hex);for(const e of H)for(const o of e.shades)Q.set(o.token,o.hex);const tt=new Map;for(const e of H)for(const o of e.shades)tt.set(o.hex.toLowerCase(),o.token);for(const e of z)tt.set(e.hex.toLowerCase(),e.token);function ut(e){return e.startsWith("#")}function _(e){return ut(e)||e==="transparent"?e:Q.get(e)??e}function L(e,o){return ut(e)?null:`${o}-${e}`}function ht(e){return tt.get(e.toLowerCase())??null}function et(e,o,t){const r=[],s=[];if(e){const i=L(e,"text");i?r.push(i):s.push(`color: ${_(e)}`)}if(o){const i=L(o,"bg");i?r.push(i):s.push(`background-color: ${_(o)}`)}if(t){const i=L(t,"border");i?r.push(i):s.push(`border-color: ${_(t)}`)}return{classes:r,style:s.join("; ")}}const N=["xs","sm","base","lg","xl","2xl","3xl","4xl","5xl","6xl","7xl"],dt={xs:12,sm:14,base:16,lg:18,xl:20,"2xl":24,"3xl":30,"4xl":36,"5xl":48,"6xl":64,"7xl":80},rt="base";function ft(e){return N.includes(e)}function E(e){return dt[e]}function ot(e){return!e||e==="base"||!ft(e)?null:`text-${e}`}function nt(e){return N.find(o=>dt[o]===e)??null}function I(e,o){const t=e&&ft(e)?e:rt,r=N.indexOf(t),s=Math.max(0,Math.min(N.length-1,r+o));return N[s]}const te=new Set(["primary","secondary","success","danger","warning","info","light","dark"]);class k extends h.ElementNode{constructor(t="#",r="primary",s=!1,i=null,a=null,c=null,l=null,f){super(f);g(this,"__href");g(this,"__variant");g(this,"__outline");g(this,"__textColor");g(this,"__bgColor");g(this,"__borderColor");g(this,"__fontSize");this.__href=t,this.__variant=r,this.__outline=s,this.__textColor=i,this.__bgColor=a,this.__borderColor=c,this.__fontSize=l}static getType(){return"bootstrap-button"}static clone(t){return new k(t.__href,t.__variant,t.__outline,t.__textColor,t.__bgColor,t.__borderColor,t.__fontSize,t.__key)}variantClass(){return this.__outline?`btn-outline-${this.__variant}`:`btn-${this.__variant}`}buildClassName(){return`btn ${this.variantClass()}`}applyInlineStyles(t){t.style.color=this.__textColor?_(this.__textColor):"",t.style.backgroundColor=this.__bgColor?_(this.__bgColor):"",t.style.borderColor=this.__borderColor?_(this.__borderColor):"",t.style.fontSize=this.__fontSize?`${E(this.__fontSize)}px`:""}createDOM(t){const r=document.createElement("a");return r.href=this.__href,r.className=this.buildClassName(),this.applyInlineStyles(r),r}updateDOM(t,r){const s=r;return t.__href!==this.__href&&(s.href=this.__href),(t.__variant!==this.__variant||t.__outline!==this.__outline)&&(s.className=this.buildClassName()),(t.__textColor!==this.__textColor||t.__bgColor!==this.__bgColor||t.__borderColor!==this.__borderColor||t.__fontSize!==this.__fontSize)&&this.applyInlineStyles(s),!1}static importDOM(){return{a:t=>t.classList.contains("btn")?{conversion:ee,priority:1}:null}}exportDOM(){const t=document.createElement("a");t.setAttribute("href",this.__href);const{classes:r,style:s}=et(this.__textColor,this.__bgColor,this.__borderColor);t.className=[this.buildClassName(),...r].join(" ");const i=s?[s]:[];return this.__fontSize&&i.push(`font-size: ${E(this.__fontSize)}px`),i.length&&t.setAttribute("style",i.join("; ")),{element:t}}static importJSON(t){const r=R({href:t.href,variant:t.variant,outline:t.outline,textColor:t.textColor??null,bgColor:t.bgColor??null,borderColor:t.borderColor??null,fontSize:t.fontSize??null});return r.setFormat(t.format),r.setIndent(t.indent),r.setDirection(t.direction),r}exportJSON(){return{...super.exportJSON(),type:"bootstrap-button",version:1,href:this.getHref(),variant:this.getVariant(),outline:this.getOutline(),textColor:this.__textColor,bgColor:this.__bgColor,borderColor:this.__borderColor,fontSize:this.__fontSize}}getHref(){return this.getLatest().__href}setHref(t){const r=this.getWritable();return r.__href=t,r}getVariant(){return this.getLatest().__variant}setVariant(t){const r=this.getWritable();return r.__variant=t,r}getOutline(){return this.getLatest().__outline}setOutline(t){const r=this.getWritable();return r.__outline=t,r}getTextColor(){return this.getLatest().__textColor}setTextColor(t){const r=this.getWritable();return r.__textColor=t,r}getBgColor(){return this.getLatest().__bgColor}setBgColor(t){const r=this.getWritable();return r.__bgColor=t,r}getBorderColor(){return this.getLatest().__borderColor}setBorderColor(t){const r=this.getWritable();return r.__borderColor=t,r}getFontSize(){return this.getLatest().__fontSize}setFontSize(t){const r=this.getWritable();return r.__fontSize=t,r}isInline(){return!0}canBeEmpty(){return!1}canInsertTextBefore(){return!1}canInsertTextAfter(){return!1}}function ee(e){const o=e,t=Array.from(o.classList).some(i=>i.startsWith("btn-outline-"));let r="primary";for(const i of Array.from(o.classList)){const a=i.replace(/^btn-outline-/,"").replace(/^btn-/,"");if(a!==i&&te.has(a)){r=a;break}}return{node:R({href:o.getAttribute("href")??"#",variant:r,outline:t})}}function R(e={}){const{href:o="#",variant:t="primary",outline:r=!1,textColor:s=null,bgColor:i=null,borderColor:a=null,fontSize:c=null}=e;return h.$applyNodeReplacement(new k(o,t,r,s,i,a,c))}function bt(e,o={}){const t=R(o);return t.append(h.$createTextNode(e)),t}function F(e){return e instanceof k}function P(e){switch(e){case"center":return"center";case"right":return"right";case"justify":return"justify";case"left":return"left";case"start":return"left";case"end":return"right";default:return null}}function mt(e){const o=P(e);return o?`text-${o}`:null}function pt(e){const o=P(e);return!o||o==="justify"?null:`ax-${o}`}class S extends h.ParagraphNode{constructor(t=null,r=null,s=null,i){super(i);g(this,"__textColor");g(this,"__bgColor");g(this,"__fontSize");this.__textColor=t,this.__bgColor=r,this.__fontSize=s}static getType(){return"bootstrap-paragraph"}static clone(t){return new S(t.__textColor,t.__bgColor,t.__fontSize,t.__key)}static importJSON(t){const r=new S(t.textColor??null,t.bgColor??null,t.fontSize??null);return r.setFormat(t.format),r.setIndent(t.indent),r.setDirection(t.direction),r}exportJSON(){return{...super.exportJSON(),type:"bootstrap-paragraph",version:1,textColor:this.__textColor,bgColor:this.__bgColor,fontSize:this.__fontSize}}getFontSize(){return this.getLatest().__fontSize}setFontSize(t){const r=this.getWritable();return r.__fontSize=t,r}getTextColor(){return this.getLatest().__textColor}setTextColor(t){const r=this.getWritable();return r.__textColor=t,r}getBgColor(){return this.getLatest().__bgColor}setBgColor(t){const r=this.getWritable();return r.__bgColor=t,r}applyInlineStyles(t){t.style.color=this.__textColor?_(this.__textColor):"",t.style.backgroundColor=this.__bgColor?_(this.__bgColor):"",t.style.fontSize=this.__fontSize?`${E(this.__fontSize)}px`:""}createDOM(t){const r=super.createDOM(t);return this.applyInlineStyles(r),r}updateDOM(t,r,s){return super.updateDOM(t,r,s)?!0:(this.applyInlineStyles(r),!1)}hasButtonChild(){return this.getChildren().some(t=>F(t))}exportDOM(){const t=document.createElement("div"),r=[],s=this.hasButtonChild()?pt(this.getFormatType()):mt(this.getFormatType());s&&r.push(s);const{classes:i,style:a}=et(this.__textColor,this.__bgColor);r.push(...i);const c=ot(this.__fontSize);return c&&r.push(c),r.length&&(t.className=r.join(" ")),a&&t.setAttribute("style",a),this.getChildrenSize()===0&&t.appendChild(document.createElement("br")),{element:t}}}const M=[{key:"0",px:0},{key:"1",px:4},{key:"2",px:8},{key:"3",px:12},{key:"4",px:16},{key:"5",px:20},{key:"6",px:24},{key:"7",px:28},{key:"8",px:32},{key:"9",px:36},{key:"10",px:40},{key:"12",px:48},{key:"16",px:64},{key:"20",px:80},{key:"24",px:96},{key:"32",px:128},{key:"40",px:160},{key:"48",px:192},{key:"56",px:224},{key:"64",px:256},{key:"80",px:320},{key:"96",px:384},{key:"112",px:448},{key:"128",px:512},{key:"144",px:576},{key:"150",px:600}];function A(e){var o;return e?((o=M.find(t=>t.key===e))==null?void 0:o.px)??null:null}function gt(e,o,t){if(e==="fixed"){const r=[];return o&&r.push(`w-${o}`),t&&r.push(`h-${t}`),r.length?r:["img-fluid"]}return e==="max"?o?[`max-w-${o}`,"w-full"]:["img-fluid"]:["img-fluid"]}function re(e){let o="fluid",t=null,r=null;for(const s of e){const i=s.match(/^max-w-(.+)$/),a=s.match(/^w-(.+)$/),c=s.match(/^h-(.+)$/);i&&i[1]!=="full"?(o="max",t=i[1]):a&&a[1]!=="full"&&a[1]!=="auto"?(o="fixed",t=a[1]):c&&c[1]!=="full"&&c[1]!=="auto"&&(o="fixed",r=c[1])}return{mode:o,width:t,height:r}}function xt(e,o,t){if(e==="fixed"){const r=A(o),s=A(t);return r!==null?{width:`${r}px`,height:"auto"}:s!==null?{width:"auto",height:`${s}px`}:{width:"auto",height:"auto"}}if(e==="max"){const r=A(o);return{maxWidth:r!==null?`${r}px`:"100%",width:"100%",height:"auto"}}return{maxWidth:"100%",width:"100%",height:"auto"}}function D({label:e,steps:o,value:t,fallbackKey:r,onChange:s}){var l;const i=t??r,a=Math.max(0,o.findIndex(f=>f.key===i)),c=((l=o[a])==null?void 0:l.px)??0;return n.jsxs("label",{className:"bew-slider",children:[n.jsxs("span",{className:"bew-slider-head",children:[e,n.jsxs("span",{className:"bew-slider-px",children:[c,"px"]})]}),n.jsx("input",{type:"range",min:0,max:o.length-1,step:1,value:a,onChange:f=>s(o[Number(f.target.value)].key)})]})}const oe=[{mode:"fluid",label:"Full width"},{mode:"fixed",label:"Fixed size"},{mode:"max",label:"Max width"}];function ne({nodeKey:e,src:o,alt:t,mode:r,width:s,height:i}){const[a]=O.useLexicalComposerContext(),[c,l]=m.useState(!1),f=d=>{a.update(()=>{const p=h.$getNodeByKey(e);_t(p)&&d(p)})},x=d=>{f(p=>{p.setMode(d),d==="fluid"?(p.setWidth(null),p.setHeight(null)):d==="fixed"?!p.getWidth()&&!p.getHeight()&&p.setWidth("64"):d==="max"&&(p.setHeight(null),p.getWidth()||p.setWidth("96"))})},b=i!==null?"height":"width";return n.jsxs("div",{className:"bew-image-container",children:[n.jsxs("span",{className:"bew-image-wrap",draggable:!1,children:[n.jsx("img",{src:o,alt:t,className:"bew-image",style:xt(r,s,i)}),n.jsx("button",{type:"button",className:"bew-image-gear","aria-label":"Image settings",onClick:()=>l(d=>!d),children:"⚙"})]}),c&&n.jsxs("div",{className:"bew-image-panel",children:[n.jsx("div",{className:"bew-image-modes",children:oe.map(d=>n.jsx("button",{type:"button",className:`bew-image-mode${r===d.mode?" is-active":""}`,onClick:()=>x(d.mode),children:d.label},d.mode))}),r==="fixed"&&n.jsxs(n.Fragment,{children:[n.jsxs("div",{className:"bew-image-modes",children:[n.jsx("button",{type:"button",className:`bew-image-mode${b==="width"?" is-active":""}`,onClick:()=>f(d=>{d.setHeight(null),d.getWidth()||d.setWidth("64")}),children:"Width"}),n.jsx("button",{type:"button",className:`bew-image-mode${b==="height"?" is-active":""}`,onClick:()=>f(d=>{d.setWidth(null),d.getHeight()||d.setHeight("48")}),children:"Height"})]}),n.jsx(D,{label:b==="width"?"Width":"Height",steps:M,value:b==="width"?s:i,fallbackKey:b==="width"?"64":"48",onChange:d=>f(p=>{b==="width"?(p.setWidth(d),p.setHeight(null)):(p.setHeight(d),p.setWidth(null))})})]}),r==="max"&&n.jsx(D,{label:"Max width",steps:M,value:s,fallbackKey:"96",onChange:d=>f(p=>p.setWidth(d))})]})]})}class j extends h.DecoratorNode{constructor(t,r="",s="fluid",i=null,a=null,c){super(c);g(this,"__src");g(this,"__alt");g(this,"__mode");g(this,"__width");g(this,"__height");this.__src=t,this.__alt=r,this.__mode=s,this.__width=i,this.__height=a}static getType(){return"bootstrap-image"}static clone(t){return new j(t.__src,t.__alt,t.__mode,t.__width,t.__height,t.__key)}static importJSON(t){return K({src:t.src,alt:t.alt,mode:t.mode,width:t.width,height:t.height})}exportJSON(){return{type:"bootstrap-image",version:1,src:this.__src,alt:this.__alt,mode:this.__mode,width:this.__width,height:this.__height}}static importDOM(){return{img:()=>({conversion:se,priority:0})}}exportDOM(){const t=document.createElement("img");return t.setAttribute("src",this.__src),t.setAttribute("alt",this.__alt),t.className=gt(this.__mode,this.__width,this.__height).join(" "),{element:t}}getSrc(){return this.getLatest().__src}setSrc(t){const r=this.getWritable();return r.__src=t,r}getAlt(){return this.getLatest().__alt}setAlt(t){const r=this.getWritable();return r.__alt=t,r}getMode(){return this.getLatest().__mode}setMode(t){const r=this.getWritable();return r.__mode=t,r}getWidth(){return this.getLatest().__width}setWidth(t){const r=this.getWritable();return r.__width=t,r}getHeight(){return this.getLatest().__height}setHeight(t){const r=this.getWritable();return r.__height=t,r}createDOM(){const t=document.createElement("div");return t.className="bew-image-block",t}updateDOM(){return!1}isInline(){return!1}decorate(){return n.jsx(ne,{nodeKey:this.getKey(),src:this.__src,alt:this.__alt,mode:this.__mode,width:this.__width,height:this.__height})}}function se(e){const o=e,t=o.getAttribute("src");if(!t)return null;const r=o.getAttribute("alt")??"",{mode:s,width:i,height:a}=re(Array.from(o.classList));return{node:K({src:t,alt:r,mode:s,width:i,height:a})}}function K(e){const{src:o,alt:t="",mode:r="fluid",width:s=null,height:i=null,key:a}=e;return h.$applyNodeReplacement(new j(o,t,r,s,i,a))}function _t(e){return e instanceof j}const W=[{key:"0",px:0},{key:"1",px:4},{key:"2",px:8},{key:"3",px:12},{key:"4",px:16},{key:"5",px:20},{key:"6",px:24},{key:"7",px:28},{key:"8",px:32},{key:"9",px:36},{key:"10",px:40},{key:"12",px:48},{key:"16",px:64},{key:"20",px:80},{key:"24",px:96},{key:"32",px:128},{key:"40",px:160}],C="5";function V(e){var o;return((o=W.find(t=>t.key===e))==null?void 0:o.px)??0}function Ct(e,o){return[`mt-${e}`,`mb-${o}`]}function ie(e){let o=C,t=C;for(const r of e){const s=r.match(/^my-(.+)$/),i=r.match(/^mt-(.+)$/),a=r.match(/^mb-(.+)$/);s?(o=s[1],t=s[1]):i?o=i[1]:a&&(t=a[1])}return{top:o,bottom:t}}function ae({nodeKey:e,top:o,bottom:t}){const[r]=O.useLexicalComposerContext(),[s,i]=m.useState(!1),a=c=>{r.update(()=>{const l=h.$getNodeByKey(e);yt(l)&&c(l)})};return n.jsxs("div",{className:"bew-hr-container",children:[n.jsx("hr",{className:"bew-hr",style:{marginTop:`${V(o)}px`,marginBottom:`${V(t)}px`}}),n.jsx("button",{type:"button",className:"bew-hr-gear","aria-label":"Separator settings",onClick:()=>i(c=>!c),children:"⚙"}),s&&n.jsxs("div",{className:"bew-hr-panel",children:[n.jsx(D,{label:"Top spacing",steps:W,value:o,fallbackKey:C,onChange:c=>a(l=>l.setTop(c))}),n.jsx(D,{label:"Bottom spacing",steps:W,value:t,fallbackKey:C,onChange:c=>a(l=>l.setBottom(c))})]})]})}class $ extends h.DecoratorNode{constructor(t=C,r=C,s){super(s);g(this,"__top");g(this,"__bottom");this.__top=t,this.__bottom=r}static getType(){return"bootstrap-hr"}static clone(t){return new $(t.__top,t.__bottom,t.__key)}static importJSON(t){return J({top:t.top,bottom:t.bottom})}exportJSON(){return{type:"bootstrap-hr",version:1,top:this.__top,bottom:this.__bottom}}static importDOM(){return{hr:()=>({conversion:le,priority:0})}}exportDOM(){const t=document.createElement("hr");return t.className=Ct(this.__top,this.__bottom).join(" "),{element:t}}getTop(){return this.getLatest().__top}setTop(t){const r=this.getWritable();return r.__top=t,r}getBottom(){return this.getLatest().__bottom}setBottom(t){const r=this.getWritable();return r.__bottom=t,r}createDOM(){const t=document.createElement("div");return t.className="bew-hr-block",t}updateDOM(){return!1}isInline(){return!1}decorate(){return n.jsx(ae,{nodeKey:this.getKey(),top:this.__top,bottom:this.__bottom})}}function le(e){const{top:o,bottom:t}=ie(Array.from(e.classList));return{node:J({top:o,bottom:t})}}function J(e={}){const{top:o=C,bottom:t=C,key:r}=e;return h.$applyNodeReplacement(new $(o,t,r))}function yt(e){return e instanceof $}function wt(e,o={}){const{label:t="Button",...r}=o;e.update(()=>{const s=h.$getSelection();h.$isRangeSelection(s)&&h.$insertNodes([bt(t,r)])})}function St(e,o){e.update(()=>{Z.$insertNodeToNearestRoot(K(o))})}function Nt(e,o={}){e.update(()=>{Z.$insertNodeToNearestRoot(J(o))})}const ce={variant:"primary",outline:!1,textColor:null,bgColor:null,borderColor:null,fontSize:null};let kt={...ce};function jt(){return{...kt}}function st(e){kt={...e}}function $t(e){return{variant:e.getVariant(),outline:e.getOutline(),textColor:e.getTextColor(),bgColor:e.getBgColor(),borderColor:e.getBorderColor(),fontSize:e.getFontSize()}}function ue(e){return e!==null&&typeof e.setTextColor=="function"}function he(e,o,t){o==="text"?e.setTextColor(t):o==="bg"&&e.setBgColor(t)}function de(e,o,t){const r=o==="text"?e.setTextColor(t):o==="bg"?e.setBgColor(t):e.setBorderColor(t);st($t(r))}function vt(e,o){const t=h.$getSelection();if(!h.$isRangeSelection(t))return;const r=t.anchor.getNode();let s=r;for(;s!==null;){if(F(s)){de(s,e,o);return}s=s.getParent()}if(e==="border")return;if(t.isCollapsed()){const a=r.getKey()==="root"?null:r.getTopLevelElementOrThrow();ue(a)&&he(a,e,o);return}const i=e==="text"?"color":"background-color";Y.$patchStyleText(t,{[i]:o?_(o):null})}function Lt(e,o,t){e.update(()=>vt(o,t))}function fe(e){return e!==null&&typeof e.setFontSize=="function"}function lt(e){return e===rt?null:e}function Tt(e){const o=e==="increase"?1:-1,t=h.$getSelection();if(!h.$isRangeSelection(t))return;const r=t.anchor.getNode();let s=r;for(;s!==null;){if(F(s)){const f=I(s.getFontSize(),o),x=s.setFontSize(lt(f));st($t(x));return}s=s.getParent()}if(t.isCollapsed()){const f=r.getKey()==="root"?null:r.getTopLevelElementOrThrow();if(fe(f)){const x=I(f.getFontSize(),o);f.setFontSize(lt(x))}return}const i=Y.$getSelectionStyleValueForProperty(t,"font-size",""),a=parseInt(i,10),c=Number.isNaN(a)?null:nt(a),l=I(c,o);Y.$patchStyleText(t,{"font-size":l===rt?null:`${E(l)}px`})}function Et(e,o){e.update(()=>Tt(o))}const Ot="bew:custom-colors",X=new Set;let w=null;function U(){if(w)return w;try{if(typeof localStorage<"u"){const e=localStorage.getItem(Ot);w=e?JSON.parse(e):[]}else w=[]}catch{w=[]}return w}function It(e){w=e;try{typeof localStorage<"u"&&localStorage.setItem(Ot,JSON.stringify(e))}catch{}X.forEach(o=>o(e))}function be(){return U()}function At(e){const o=e.toLowerCase(),t=U();t.includes(o)||It([...t,o])}function Bt(e){const o=e.toLowerCase();It(U().filter(t=>t!==o))}function Mt(){const[e,o]=m.useState(U);return m.useEffect(()=>(X.add(o),()=>{X.delete(o)}),[]),e}function q({hex:e,token:o,title:t,onSelect:r,onRemove:s}){const i=e==="transparent";return n.jsx("button",{type:"button",className:`bew-swatch${i?" bew-swatch--transparent":""}`,style:i?void 0:{backgroundColor:e},title:t,"aria-label":t,onClick:()=>r(o),onContextMenu:s?a=>{a.preventDefault(),s()}:void 0})}function B({onSelect:e}){const o=Mt(),t=m.useRef(null);return n.jsxs("div",{className:"bew-color-picker",children:[n.jsxs("button",{type:"button",className:"bew-color-clear",onClick:()=>e(null),children:[n.jsx("span",{className:"bew-color-clear-swatch"}),"No color"]}),n.jsx("div",{className:"bew-color-row",children:z.map(r=>n.jsx(q,{hex:r.hex,token:r.token,title:r.token,onSelect:e},r.token))}),n.jsx("div",{className:"bew-color-grid",children:H.map(r=>n.jsx("div",{className:"bew-color-grid-row",children:[...r.shades].reverse().map(s=>n.jsx(q,{hex:s.hex,token:s.token,title:s.token,onSelect:e},s.token))},r.name))}),n.jsxs("div",{className:"bew-color-custom",children:[n.jsx("span",{className:"bew-color-custom-label",children:"Custom"}),n.jsxs("div",{className:"bew-color-row",children:[o.map(r=>n.jsx(q,{hex:r,token:r,title:`${r} (right-click to remove)`,onSelect:e,onRemove:()=>Bt(r)},r)),n.jsx("button",{type:"button",className:"bew-swatch bew-swatch--add",title:"Add a custom color","aria-label":"Add a custom color",onClick:()=>{var r;return(r=t.current)==null?void 0:r.click()},children:"+"}),n.jsx("input",{ref:t,type:"color",className:"bew-color-input",onChange:r=>{const s=r.target.value.toLowerCase();At(s),e(s)}})]})]})]})}function v(...e){return e.filter(Boolean).join(" ")}function me(){return n.jsx("svg",{className:"bew-tb-chevron",width:"10",height:"10",viewBox:"0 0 10 10","aria-hidden":"true",children:n.jsx("path",{d:"M2 3.5L5 6.5L8 3.5",fill:"none",stroke:"currentColor",strokeWidth:"1.4",strokeLinecap:"round",strokeLinejoin:"round"})})}const pe={left:[[1,15],[1,9],[1,15],[1,9]],center:[[1,15],[4,12],[1,15],[4,12]],right:[[1,15],[7,15],[1,15],[7,15]],justify:[[1,15],[1,15],[1,15],[1,15]]};function ge({align:e}){return n.jsx("svg",{width:"16",height:"16",viewBox:"0 0 16 16","aria-hidden":"true",children:pe[e].map(([o,t],r)=>n.jsx("line",{x1:o,x2:t,y1:3+r*3.5,y2:3+r*3.5,stroke:"currentColor",strokeWidth:"1.4",strokeLinecap:"round"},r))})}const xe=["left","center","right","justify"];function G({label:e,ariaLabel:o,children:t,triggerClassName:r,menuClassName:s}){const[i,a]=m.useState(!1),c=m.useRef(null);return m.useEffect(()=>{if(!i)return;const l=f=>{c.current&&!c.current.contains(f.target)&&a(!1)};return document.addEventListener("mousedown",l),()=>document.removeEventListener("mousedown",l)},[i]),n.jsxs("div",{className:"bew-tb-dropdown",ref:c,children:[n.jsxs("button",{type:"button",className:r??"bew-tb-dropdown-trigger","aria-label":o,"aria-expanded":i,onClick:()=>a(l=>!l),children:[r?e:n.jsx("span",{className:"bew-tb-dropdown-label",children:e}),!r&&n.jsx(me,{})]}),i&&n.jsx("div",{className:`bew-tb-menu${s?` ${s}`:""}`,onClick:()=>a(!1),children:t})]})}function _e(){return n.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 16 16","aria-hidden":"true",children:[n.jsx("path",{d:"M3.5 12L6.5 4h1L10.5 12M4.6 9.2h4.3",fill:"none",stroke:"currentColor",strokeWidth:"1.3",strokeLinecap:"round",strokeLinejoin:"round"}),n.jsx("rect",{x:"2.5",y:"13",width:"9",height:"2",rx:"0.5",className:"bew-tb-colorbar"})]})}function Ce(){return n.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 16 16","aria-hidden":"true",children:[n.jsx("path",{d:"M6 2.5l5 5-4.2 4.2a1.5 1.5 0 0 1-2.1 0L2.5 9.5a1.5 1.5 0 0 1 0-2.1z",fill:"none",stroke:"currentColor",strokeWidth:"1.2",strokeLinejoin:"round"}),n.jsx("path",{d:"M4 8.5h7",fill:"none",stroke:"currentColor",strokeWidth:"1.2"}),n.jsx("rect",{x:"2.5",y:"13",width:"11",height:"2",rx:"0.5",className:"bew-tb-colorbar"})]})}function ye(){return n.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 16 16","aria-hidden":"true",children:[n.jsx("rect",{x:"2.5",y:"2.5",width:"8",height:"8",rx:"1",fill:"none",stroke:"currentColor",strokeWidth:"1.3",strokeDasharray:"2 1.4"}),n.jsx("rect",{x:"2.5",y:"13",width:"11",height:"2",rx:"0.5",className:"bew-tb-colorbar"})]})}function we(){return n.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 16 16","aria-hidden":"true",children:[n.jsx("rect",{x:"1.5",y:"2.5",width:"13",height:"11",rx:"1.5",fill:"none",stroke:"currentColor",strokeWidth:"1.2"}),n.jsx("circle",{cx:"5.5",cy:"6",r:"1.3",fill:"currentColor"}),n.jsx("path",{d:"M2.5 12l3.5-4 2.5 2.5L11 8l3 3.5",fill:"none",stroke:"currentColor",strokeWidth:"1.2",strokeLinejoin:"round"})]})}function Se(){return n.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 16 16","aria-hidden":"true",children:[n.jsx("line",{x1:"2",y1:"8",x2:"14",y2:"8",stroke:"currentColor",strokeWidth:"1.4",strokeLinecap:"round"}),n.jsx("line",{x1:"3.5",y1:"4.5",x2:"12.5",y2:"4.5",stroke:"currentColor",strokeWidth:"1.1",strokeLinecap:"round",opacity:"0.35"}),n.jsx("line",{x1:"3.5",y1:"11.5",x2:"12.5",y2:"11.5",stroke:"currentColor",strokeWidth:"1.1",strokeLinecap:"round",opacity:"0.35"})]})}function Ne(){return n.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 16 16","aria-hidden":"true",children:[n.jsx("rect",{x:"1.5",y:"4.5",width:"13",height:"7",rx:"2",fill:"currentColor",opacity:"0.15"}),n.jsx("rect",{x:"1.5",y:"4.5",width:"13",height:"7",rx:"2",fill:"none",stroke:"currentColor",strokeWidth:"1.2"}),n.jsx("path",{d:"M5.5 8h5M8 5.5v5",stroke:"currentColor",strokeWidth:"1.2",strokeLinecap:"round"})]})}function Dt(){const[e]=O.useLexicalComposerContext(),[o,t]=m.useState({bold:!1,italic:!1,underline:!1,strikethrough:!1}),[r,s]=m.useState("left"),[i,a]=m.useState(!1),[c,l]=m.useState(!1),f=m.useCallback(()=>{const u=h.$getSelection();if(!h.$isRangeSelection(u))return;t({bold:u.hasFormat("bold"),italic:u.hasFormat("italic"),underline:u.hasFormat("underline"),strikethrough:u.hasFormat("strikethrough")});const y=u.anchor.getNode(),at=y.getKey()==="root"?y:y.getTopLevelElementOrThrow();s(h.$isElementNode(at)?P(at.getFormatType())??"left":"left")},[]);m.useEffect(()=>Z.mergeRegister(e.registerUpdateListener(({editorState:u})=>{u.read(f)}),e.registerCommand(h.SELECTION_CHANGE_COMMAND,()=>(f(),!1),h.COMMAND_PRIORITY_LOW),e.registerCommand(h.CAN_UNDO_COMMAND,u=>(a(u),!1),h.COMMAND_PRIORITY_LOW),e.registerCommand(h.CAN_REDO_COMMAND,u=>(l(u),!1),h.COMMAND_PRIORITY_LOW)),[e,f]);const x=m.useCallback(u=>{Et(e,u)},[e]),b=m.useCallback(u=>{e.dispatchCommand(h.FORMAT_TEXT_COMMAND,u)},[e]),d=m.useCallback(u=>{e.dispatchCommand(h.FORMAT_ELEMENT_COMMAND,u)},[e]),p=m.useCallback((u,y)=>{Lt(e,u,y)},[e]),Ft=m.useCallback(()=>{wt(e,{...jt(),label:"Button"})},[e]),Pt=m.useCallback(()=>{const u=window.prompt("Image URL"),y=u==null?void 0:u.trim();y&&St(e,{src:y})},[e]),Kt=m.useCallback(()=>{Nt(e)},[e]);return n.jsxs("div",{className:"bew-toolbar",role:"toolbar","aria-label":"Formatting",children:[n.jsx("button",{type:"button",className:"bew-tb-btn","aria-label":"Undo",disabled:!i,onClick:()=>e.dispatchCommand(h.UNDO_COMMAND,void 0),children:"↶"}),n.jsx("button",{type:"button",className:"bew-tb-btn","aria-label":"Redo",disabled:!c,onClick:()=>e.dispatchCommand(h.REDO_COMMAND,void 0),children:"↷"}),n.jsx("span",{className:"bew-tb-divider"}),n.jsxs("button",{type:"button",className:"bew-tb-btn bew-tb-font","aria-label":"Decrease font size",title:"Decrease font size",onClick:()=>x("decrease"),children:[n.jsx("span",{className:"bew-tb-font-a bew-tb-font-a--sm",children:"A"}),n.jsx("span",{className:"bew-tb-font-sign",children:"−"})]}),n.jsxs("button",{type:"button",className:"bew-tb-btn bew-tb-font","aria-label":"Increase font size",title:"Increase font size",onClick:()=>x("increase"),children:[n.jsx("span",{className:"bew-tb-font-a bew-tb-font-a--lg",children:"A"}),n.jsx("span",{className:"bew-tb-font-sign",children:"+"})]}),n.jsx("span",{className:"bew-tb-divider"}),n.jsx("button",{type:"button",className:v("bew-tb-btn","bew-tb-btn--bold",o.bold&&"is-active"),"aria-label":"Bold","aria-pressed":o.bold,onClick:()=>b("bold"),children:"B"}),n.jsx("button",{type:"button",className:v("bew-tb-btn","bew-tb-btn--italic",o.italic&&"is-active"),"aria-label":"Italic","aria-pressed":o.italic,onClick:()=>b("italic"),children:"I"}),n.jsx("button",{type:"button",className:v("bew-tb-btn","bew-tb-btn--underline",o.underline&&"is-active"),"aria-label":"Underline","aria-pressed":o.underline,onClick:()=>b("underline"),children:"U"}),n.jsx("button",{type:"button",className:v("bew-tb-btn","bew-tb-btn--strike",o.strikethrough&&"is-active"),"aria-label":"Strikethrough","aria-pressed":o.strikethrough,onClick:()=>b("strikethrough"),children:"S"}),n.jsx("span",{className:"bew-tb-divider"}),xe.map(u=>n.jsx("button",{type:"button",className:v("bew-tb-btn",r===u&&"is-active"),"aria-label":`Align ${u}`,"aria-pressed":r===u,onClick:()=>d(u),children:n.jsx(ge,{align:u})},u)),n.jsx("span",{className:"bew-tb-divider"}),n.jsx(G,{ariaLabel:"Text color",triggerClassName:"bew-tb-btn",menuClassName:"bew-tb-menu--color",label:n.jsx(_e,{}),children:n.jsx(B,{onSelect:u=>p("text",u)})}),n.jsx(G,{ariaLabel:"Background color",triggerClassName:"bew-tb-btn",menuClassName:"bew-tb-menu--color",label:n.jsx(Ce,{}),children:n.jsx(B,{onSelect:u=>p("bg",u)})}),n.jsx(G,{ariaLabel:"Border color (buttons)",triggerClassName:"bew-tb-btn",menuClassName:"bew-tb-menu--color",label:n.jsx(ye,{}),children:n.jsx(B,{onSelect:u=>p("border",u)})}),n.jsx("span",{className:"bew-tb-divider"}),n.jsx("button",{type:"button",className:"bew-tb-btn","aria-label":"Insert button",title:"Insert button",onClick:Ft,children:n.jsx(Ne,{})}),n.jsx("button",{type:"button",className:"bew-tb-btn","aria-label":"Insert image",title:"Insert image from URL",onClick:Pt,children:n.jsx(we,{})}),n.jsx("button",{type:"button",className:"bew-tb-btn","aria-label":"Insert separator",title:"Insert separator",onClick:Kt,children:n.jsx(Se,{})})]})}const ke=new Set(["DIV","P","H1","H2","H3","H4","H5","H6","TABLE","THEAD","TBODY","TR","TD","TH","UL","OL","LI"]),Wt=new Set(["BR","HR","IMG"]);function T(e){return Array.from(e.attributes).map(o=>` ${o.name}="${o.value}"`).join("")}function je(e){const o=e.trim().toLowerCase(),t=o.match(/^rgba?\(([^)]+)\)$/);if(!t)return o;const[r,s,i]=t[1].split(",").map(c=>c.trim()),a=c=>Math.max(0,Math.min(255,parseInt(c,10))).toString(16).padStart(2,"0");return`#${a(r)}${a(s)}${a(i)}`}function $e(e){e.querySelectorAll("[style]").forEach(o=>{const t=o.tagName==="A"&&o.classList.contains("btn"),r=(o.getAttribute("style")??"").split(";").map(i=>i.trim()).filter(Boolean),s=[];for(const i of r){const[a,...c]=i.split(":"),l=a.trim().toLowerCase(),f=c.join(":").trim(),x=l==="color"?"text":l==="background-color"?"bg":l==="border-color"?"border":null;if(x){const b=je(f),d=ht(b),p=d?L(d,x):null;p?o.classList.add(p):s.push(`${l}: ${b}`);continue}if(l==="font-size"&&!t){const b=ot(nt(parseInt(f,10)));if(b){o.classList.add(b);continue}}s.push(i)}s.length?o.setAttribute("style",s.join("; ")):o.removeAttribute("style")})}function ve(e){e.querySelectorAll("span").forEach(o=>{const t=o.getAttribute("style")??"";if(t.includes("white-space")){const r=t.split(";").map(s=>s.trim()).filter(s=>s&&!s.toLowerCase().startsWith("white-space")).join("; ");r?o.setAttribute("style",r):o.removeAttribute("style")}o.attributes.length===0&&o.replaceWith(...Array.from(o.childNodes))})}function zt(e){if(e.nodeType===Node.TEXT_NODE)return e.textContent??"";if(e.nodeType!==Node.ELEMENT_NODE)return"";const o=e,t=o.tagName.toLowerCase();if(Wt.has(o.tagName))return`<${t}${T(o)}>`;const r=Array.from(o.childNodes).map(zt).join("");return`<${t}${T(o)}>${r}</${t}>`}function Ht(e,o){const t=" ".repeat(o);if(e.nodeType===Node.TEXT_NODE){const l=e.textContent??"";return l.trim()?`${t}${l.trim()}`:""}if(e.nodeType!==Node.ELEMENT_NODE)return"";const r=e,s=r.tagName.toLowerCase();if(Wt.has(r.tagName))return`${t}<${s}${T(r)}>`;const i=Array.from(r.childNodes);if(!i.some(l=>l.nodeType===Node.ELEMENT_NODE&&ke.has(l.tagName))){const l=i.map(zt).join("");return`${t}<${s}${T(r)}>${l}</${s}>`}const c=i.map(l=>Ht(l,o+1)).filter(Boolean).join(`
2
+ `);return`${t}<${s}${T(r)}>
3
+ ${c}
4
+ ${t}</${s}>`}function Rt(e,o=!0){const t=document.createElement("div");return t.innerHTML=e,$e(t),ve(t),o?Array.from(t.childNodes).map(r=>Ht(r,0)).filter(Boolean).join(`
5
+ `):t.innerHTML}function Le(e,o){return`<!DOCTYPE html>
6
+ <html>
7
+ <head>
8
+ <meta charset="utf-8">
9
+ <meta name="viewport" content="width=device-width, initial-scale=1">
10
+ </head>
11
+ <body>
12
+ <div class="container">
13
+ ${o?e.split(`
14
+ `).map(r=>` ${r}`).join(`
15
+ `):e}
16
+ </div>
17
+ </body>
18
+ </html>`}function it(e,o={}){const{document:t=!1,pretty:r=!0}=o;let s="";e.getEditorState().read(()=>{s=Zt.$generateHtmlFromNodes(e,null)});const i=Rt(s,r);return t?Le(i,r):i}function Te({editorRef:e}){const[o]=O.useLexicalComposerContext();return m.useEffect(()=>(e.current=o,()=>{e.current=null}),[o,e]),null}function Ee({onChange:e}){const[o]=O.useLexicalComposerContext();return m.useEffect(()=>o.registerUpdateListener(({editorState:t})=>{e({html:it(o),json:JSON.stringify(t.toJSON())})}),[o,e]),null}const Oe=m.forwardRef(function({placeholder:o="Start writing your email…",toolbar:t=!0,initialContent:r,onChange:s,onError:i,children:a},c){const l=m.useRef(null);m.useImperativeHandle(c,()=>({getHtml:b=>l.current?it(l.current,b):"",getJson:()=>l.current?JSON.stringify(l.current.getEditorState().toJSON()):"",focus:()=>{var b;return(b=l.current)==null?void 0:b.focus()},clear:()=>{var b;return(b=l.current)==null?void 0:b.update(()=>{h.$getRoot().clear()})},getEditor:()=>l.current}),[]);const f=m.useCallback(b=>{if(i)i(b);else throw b},[i]),x={namespace:"bootstrap-email-wysiwyg",theme:ct,editorState:r,nodes:[k,j,$,S,{replace:h.ParagraphNode,with:()=>new S,withKlass:S}],onError:f};return n.jsxs(qt.LexicalComposer,{initialConfig:x,children:[n.jsxs("div",{className:"bew-editor-shell",children:[t&&n.jsx(Dt,{}),n.jsx("div",{className:"bew-editor-body",children:n.jsx(Gt.RichTextPlugin,{contentEditable:n.jsx(Yt.ContentEditable,{className:"bew-content-editable"}),placeholder:n.jsx("div",{className:"bew-placeholder",children:o}),ErrorBoundary:Xt.LexicalErrorBoundary})}),n.jsx(Vt.HistoryPlugin,{})]}),n.jsx(Te,{editorRef:l}),s&&n.jsx(Ee,{onChange:s}),a]})});exports.$adjustFontSize=Tt;exports.$applyColor=vt;exports.$createButtonNode=R;exports.$createButtonWithLabel=bt;exports.$createHrNode=J;exports.$createImageNode=K;exports.$isButtonNode=F;exports.$isHrNode=yt;exports.$isImageNode=_t;exports.BASE_COLORS=z;exports.BootstrapEmailEditor=Oe;exports.BootstrapParagraphNode=S;exports.ButtonNode=k;exports.COLOR_FAMILIES=H;exports.ColorPicker=B;exports.DEFAULT_HR_MARGIN=C;exports.FONT_SIZE_KEYS=N;exports.HrNode=$;exports.ImageNode=j;exports.MARGIN_STEPS=W;exports.SIZE_STEPS=M;exports.Toolbar=Dt;exports.addCustomColor=At;exports.adjustFontSize=Et;exports.applyColor=Lt;exports.axAlignClass=pt;exports.bootstrapEmailTheme=ct;exports.cleanBootstrapHtml=Rt;exports.colorAttributes=et;exports.fontSizeClass=ot;exports.fontSizePx=E;exports.getCustomColors=be;exports.getLastButtonStyle=jt;exports.hexToToken=ht;exports.hrClasses=Ct;exports.imageClasses=gt;exports.imagePreviewStyle=xt;exports.insertButton=wt;exports.insertHr=Nt;exports.insertImage=St;exports.marginKeyToPx=V;exports.normalizeAlign=P;exports.pxToFontSizeKey=nt;exports.rememberButtonStyle=st;exports.removeCustomColor=Bt;exports.sizeKeyToPx=A;exports.stepFontSize=I;exports.textAlignClass=mt;exports.toBootstrapEmailHtml=it;exports.tokenToClass=L;exports.tokenToHex=_;exports.useCustomColors=Mt;
@@ -0,0 +1 @@
1
+ .bew-slider{display:block;margin-bottom:10px;font-size:12px;color:#495057}.bew-slider:last-child{margin-bottom:0}.bew-slider-head{display:flex;justify-content:space-between;margin-bottom:4px;font-weight:600}.bew-slider-px{color:#868e96;font-weight:400}.bew-slider input[type=range]{width:100%}.bew-image-block{margin:0 0 .5rem}.bew-image-container{position:relative}.bew-image-wrap{position:relative;display:inline-block;max-width:100%;line-height:0}.bew-image{display:block;max-width:100%}.bew-image-gear{position:absolute;top:6px;right:6px;width:26px;height:26px;display:flex;align-items:center;justify-content:center;border:none;border-radius:6px;background:#212529b8;color:#fff;font-size:14px;line-height:1;cursor:pointer;opacity:0;transition:opacity .12s ease}.bew-image-wrap:hover .bew-image-gear,.bew-image-gear:focus-visible{opacity:1}.bew-image-panel{position:absolute;top:44px;left:0;z-index:30;width:240px;padding:12px;border:1px solid #e9ecef;border-radius:10px;background:#fff;box-shadow:0 6px 24px #10182829,0 1px 2px #1018280f;line-height:1.4;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif}.bew-image-modes{display:flex;gap:4px;margin-bottom:10px}.bew-image-mode{flex:1;padding:6px 4px;border:1px solid #ced4da;border-radius:6px;background:#fff;color:#495057;font-size:11.5px;cursor:pointer}.bew-image-mode.is-active{background:#e7f1ff;border-color:#0d6efd;color:#0d6efd}.bew-hr-container{position:relative}.bew-hr{border:none;border-top:1px solid #d0d5db;background:transparent;height:0}.bew-hr-gear{position:absolute;top:50%;right:6px;transform:translateY(-50%);width:26px;height:26px;display:flex;align-items:center;justify-content:center;border:none;border-radius:6px;background:#212529b8;color:#fff;font-size:14px;line-height:1;cursor:pointer;opacity:0;transition:opacity .12s ease}.bew-hr-container:hover .bew-hr-gear,.bew-hr-gear:focus-visible{opacity:1}.bew-hr-panel{position:absolute;top:6px;right:6px;z-index:30;width:220px;padding:12px;border:1px solid #e9ecef;border-radius:10px;background:#fff;box-shadow:0 6px 24px #10182829,0 1px 2px #1018280f;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif}.bew-color-picker{display:flex;flex-direction:column;gap:8px;width:max-content;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif}.bew-color-clear{display:flex;align-items:center;gap:8px;width:100%;padding:6px 8px;border:none;border-radius:6px;background:transparent;color:#343a40;font-size:13px;cursor:pointer}.bew-color-clear:hover{background:#f1f3f5}.bew-color-clear-swatch{position:relative;width:16px;height:16px;border:1px solid #ced4da;border-radius:4px;overflow:hidden}.bew-color-clear-swatch:after{content:"";position:absolute;top:50%;left:-3px;right:-3px;height:1.5px;background:#dc3545;transform:rotate(-45deg)}.bew-color-row{display:flex;flex-wrap:wrap;gap:4px}.bew-color-grid{display:flex;flex-direction:column;gap:4px}.bew-color-grid-row{display:flex;gap:4px}.bew-swatch{width:18px;height:18px;padding:0;border:1px solid rgba(0,0,0,.12);border-radius:4px;cursor:pointer;transition:transform .08s ease,box-shadow .08s ease}.bew-swatch:hover{transform:scale(1.18);box-shadow:0 0 0 1px #fff,0 0 0 2px #0d6efd;position:relative;z-index:1}.bew-swatch--transparent{background:linear-gradient(45deg,#ccc 25%,transparent 25%) -4px 0/8px 8px,linear-gradient(-45deg,#ccc 25%,transparent 25%) -4px 0/8px 8px,linear-gradient(45deg,transparent 75%,#ccc 75%) 0 0/8px 8px,linear-gradient(-45deg,transparent 75%,#ccc 75%) 0 0/8px 8px,#fff}.bew-swatch--add{display:flex;align-items:center;justify-content:center;background:#f8f9fa;color:#868e96;font-size:14px;line-height:1}.bew-swatch--add:hover{background:#eef0f2;transform:none;box-shadow:none}.bew-color-custom{display:flex;flex-direction:column;gap:6px;padding-top:6px;border-top:1px solid #e9ecef}.bew-color-custom-label{font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:#868e96}.bew-color-input{position:absolute;width:0;height:0;opacity:0;pointer-events:none}.bew-toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:2px;padding:6px 8px;border-bottom:1px solid #e9ecef;background:#fcfcfd;border-top-left-radius:.375rem;border-top-right-radius:.375rem;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif}.bew-tb-divider{width:1px;align-self:stretch;margin:4px 6px;background:#e3e6ea}.bew-tb-btn{display:inline-flex;align-items:center;justify-content:center;min-width:30px;height:30px;padding:0 7px;border:none;border-radius:6px;background:transparent;color:#495057;font-size:15px;line-height:1;cursor:pointer;transition:background-color .12s ease,color .12s ease}.bew-tb-btn:hover:not(:disabled){background:#eef0f2;color:#212529}.bew-tb-btn:disabled{opacity:.4;cursor:default}.bew-tb-btn.is-active{background:#e7f1ff;color:#0d6efd}.bew-tb-btn--bold{font-weight:700}.bew-tb-btn--italic{font-style:italic;font-family:Georgia,Times New Roman,serif}.bew-tb-btn--underline{text-decoration:underline}.bew-tb-btn--strike{text-decoration:line-through}.bew-tb-dropdown{position:relative}.bew-tb-dropdown-trigger{display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 10px;border:none;border-radius:6px;background:transparent;color:#343a40;font-size:13.5px;font-weight:500;cursor:pointer;transition:background-color .12s ease}.bew-tb-dropdown-trigger:hover,.bew-tb-dropdown-trigger[aria-expanded=true]{background:#eef0f2}.bew-tb-dropdown-label{min-width:66px;text-align:left}.bew-tb-chevron{color:#868e96;flex:none}.bew-tb-menu{position:absolute;top:calc(100% + 4px);left:0;z-index:20;min-width:180px;padding:6px;border:1px solid #e9ecef;border-radius:10px;background:#fff;box-shadow:0 6px 24px #1018281f,0 1px 2px #1018280f}.bew-tb-menu--color{min-width:0;padding:8px}.bew-tb-colorbar{fill:#0d6efd}.bew-tb-font{gap:1px}.bew-tb-font-a{font-weight:600;line-height:1}.bew-tb-font-a--sm{font-size:11px}.bew-tb-font-a--lg{font-size:16px}.bew-tb-font-sign{font-size:10px;line-height:1;align-self:flex-start;margin-top:2px}.bew-editor-shell{position:relative;border:1px solid #ced4da;border-radius:.375rem;background:#fff;color:#212529;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif}.bew-editor-body{position:relative}.bew-content-editable{min-height:200px;padding:.75rem 1rem;outline:none}.bew-placeholder{position:absolute;top:.75rem;left:1rem;color:#adb5bd;pointer-events:none;-webkit-user-select:none;user-select:none}.bew-paragraph{margin:0 0 .5rem}.bew-text-bold{font-weight:700}.bew-text-italic{font-style:italic}.bew-text-underline{text-decoration:underline}.bew-text-strikethrough{text-decoration:line-through}.bew-text-underline.bew-text-strikethrough{text-decoration:underline line-through}.bew-content-editable .btn{display:inline-block;padding:.5rem 1rem;border:1px solid transparent;border-radius:.375rem;font-weight:500;line-height:1.5;text-decoration:none;cursor:text}.bew-content-editable .btn-primary{background-color:#0d6efd;border-color:#0d6efd;color:#fff}.bew-content-editable .btn-secondary{background-color:#6c757d;border-color:#6c757d;color:#fff}.bew-content-editable .btn-success{background-color:#198754;border-color:#198754;color:#fff}.bew-content-editable .btn-danger{background-color:#dc3545;border-color:#dc3545;color:#fff}.bew-content-editable .btn-warning{background-color:#ffc107;border-color:#ffc107;color:#000}.bew-content-editable .btn-info{background-color:#0dcaf0;border-color:#0dcaf0;color:#000}.bew-content-editable .btn-light{background-color:#f8f9fa;border-color:#f8f9fa;color:#000}.bew-content-editable .btn-dark{background-color:#212529;border-color:#212529;color:#fff}.bew-content-editable .btn-outline-primary{border-color:#0d6efd;color:#0d6efd}.bew-content-editable .btn-outline-secondary{border-color:#6c757d;color:#6c757d}.bew-content-editable .btn-outline-success{border-color:#198754;color:#198754}.bew-content-editable .btn-outline-danger{border-color:#dc3545;color:#dc3545}.bew-content-editable .btn-outline-warning{border-color:#ffc107;color:#997404}.bew-content-editable .btn-outline-info{border-color:#0dcaf0;color:#087990}.bew-content-editable .btn-outline-light{border-color:#f8f9fa;color:#6c757d}.bew-content-editable .btn-outline-dark{border-color:#212529;color:#212529}