kempo-ui 0.0.43 → 0.0.62

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.
Files changed (192) hide show
  1. package/.github/copilot-instructions.md +11 -0
  2. package/.github/workflows/publish.yml +66 -0
  3. package/CONTRIBUTING.md +70 -0
  4. package/dist/components/Dialog.js +9 -8
  5. package/dist/components/HtmlEditor.js +91 -0
  6. package/dist/components/Icon.js +2 -2
  7. package/dist/components/ShadowComponent.js +1 -1
  8. package/dist/components/SideMenu.js +1 -1
  9. package/dist/components/SidePanel.js +40 -30
  10. package/dist/components/htmlEditorControls/AlignCenter.js +12 -0
  11. package/dist/components/htmlEditorControls/AlignJustify.js +12 -0
  12. package/dist/components/htmlEditorControls/AlignLeft.js +12 -0
  13. package/dist/components/htmlEditorControls/AlignRight.js +12 -0
  14. package/dist/components/htmlEditorControls/Bold.js +12 -0
  15. package/dist/components/htmlEditorControls/BulletList.js +12 -0
  16. package/dist/components/htmlEditorControls/CharacterCount.js +18 -0
  17. package/dist/components/htmlEditorControls/ClearFormatting.js +12 -0
  18. package/dist/components/htmlEditorControls/CodeBlock.js +12 -0
  19. package/dist/components/htmlEditorControls/ControlGroup.js +14 -0
  20. package/dist/components/htmlEditorControls/ControlSpacer.js +6 -0
  21. package/dist/components/htmlEditorControls/CreateLink.js +15 -0
  22. package/dist/components/htmlEditorControls/DropdownControl.js +57 -0
  23. package/dist/components/htmlEditorControls/FormatBlock.js +12 -0
  24. package/dist/components/htmlEditorControls/HtmlEditorControl.js +44 -0
  25. package/dist/components/htmlEditorControls/InlineCode.js +12 -0
  26. package/dist/components/htmlEditorControls/InsertTable.js +15 -0
  27. package/dist/components/htmlEditorControls/Italic.js +12 -0
  28. package/dist/components/htmlEditorControls/Mode.js +9 -0
  29. package/dist/components/htmlEditorControls/NumberList.js +12 -0
  30. package/dist/components/htmlEditorControls/Strikethrough.js +12 -0
  31. package/dist/components/htmlEditorControls/TextBackgroundColor.js +97 -0
  32. package/dist/components/htmlEditorControls/TextColor.js +97 -0
  33. package/dist/components/htmlEditorControls/Underline.js +12 -0
  34. package/dist/components/htmlEditorControls/WordCount.js +18 -0
  35. package/dist/utils/formatCode.js +1 -0
  36. package/docs/components/dropdown.html +2 -2
  37. package/docs/components/html-editor.html +442 -0
  38. package/docs/components/icon.html +2 -7
  39. package/docs/components/photo-viewer.html +14 -14
  40. package/docs/components/side-menu.html +32 -0
  41. package/docs/components/side-panel-dual.html +70 -0
  42. package/docs/components/side-panel-scroll.html +176 -0
  43. package/docs/components/side-panel.html +41 -1
  44. package/docs/icons/arrow_drop_down.svg +1 -0
  45. package/docs/icons/circle.svg +1 -0
  46. package/docs/icons/code.svg +1 -0
  47. package/docs/icons/code_blocks.svg +1 -0
  48. package/docs/icons/copy.svg +1 -0
  49. package/docs/icons/dot.svg +1 -0
  50. package/docs/icons/format_align_center.svg +1 -0
  51. package/docs/icons/format_align_justify.svg +1 -0
  52. package/docs/icons/format_align_left.svg +1 -0
  53. package/docs/icons/format_align_right.svg +1 -0
  54. package/docs/icons/format_bold.svg +1 -0
  55. package/docs/icons/format_clear.svg +1 -0
  56. package/docs/icons/format_color_fill.svg +1 -0
  57. package/docs/icons/format_color_text.svg +1 -0
  58. package/docs/icons/format_h1.svg +1 -0
  59. package/docs/icons/format_h2.svg +1 -0
  60. package/docs/icons/format_h3.svg +1 -0
  61. package/docs/icons/format_h4.svg +1 -0
  62. package/docs/icons/format_h5.svg +1 -0
  63. package/docs/icons/format_h6.svg +1 -0
  64. package/docs/icons/format_italic.svg +1 -0
  65. package/docs/icons/format_list_bulleted.svg +1 -0
  66. package/docs/icons/format_list_numbered.svg +1 -0
  67. package/docs/icons/format_paragraph.svg +1 -0
  68. package/docs/icons/format_underlined.svg +1 -0
  69. package/docs/icons/help.svg +1 -0
  70. package/docs/icons/link.svg +1 -0
  71. package/docs/icons/more_vert.svg +1 -0
  72. package/docs/icons/strikethrough_s.svg +1 -0
  73. package/docs/icons/table.svg +1 -0
  74. package/docs/icons/wysiwyg.svg +1 -0
  75. package/docs/index.html +6 -0
  76. package/docs/nav-1.inc.html +1 -0
  77. package/docs/nav.inc.html +1 -0
  78. package/docs/src/components/Dialog.js +9 -8
  79. package/docs/src/components/HtmlEditor.js +91 -0
  80. package/docs/src/components/Icon.js +2 -2
  81. package/docs/src/components/ShadowComponent.js +1 -1
  82. package/docs/src/components/SideMenu.js +1 -1
  83. package/docs/src/components/SidePanel.js +40 -30
  84. package/docs/src/components/htmlEditorControls/AlignCenter.js +12 -0
  85. package/docs/src/components/htmlEditorControls/AlignJustify.js +12 -0
  86. package/docs/src/components/htmlEditorControls/AlignLeft.js +12 -0
  87. package/docs/src/components/htmlEditorControls/AlignRight.js +12 -0
  88. package/docs/src/components/htmlEditorControls/Bold.js +12 -0
  89. package/docs/src/components/htmlEditorControls/BulletList.js +12 -0
  90. package/docs/src/components/htmlEditorControls/CharacterCount.js +18 -0
  91. package/docs/src/components/htmlEditorControls/ClearFormatting.js +12 -0
  92. package/docs/src/components/htmlEditorControls/CodeBlock.js +12 -0
  93. package/docs/src/components/htmlEditorControls/ControlGroup.js +14 -0
  94. package/docs/src/components/htmlEditorControls/ControlSpacer.js +6 -0
  95. package/docs/src/components/htmlEditorControls/CreateLink.js +15 -0
  96. package/docs/src/components/htmlEditorControls/DropdownControl.js +57 -0
  97. package/docs/src/components/htmlEditorControls/FormatBlock.js +12 -0
  98. package/docs/src/components/htmlEditorControls/HtmlEditorControl.js +44 -0
  99. package/docs/src/components/htmlEditorControls/InlineCode.js +12 -0
  100. package/docs/src/components/htmlEditorControls/InsertTable.js +15 -0
  101. package/docs/src/components/htmlEditorControls/Italic.js +12 -0
  102. package/docs/src/components/htmlEditorControls/Mode.js +9 -0
  103. package/docs/src/components/htmlEditorControls/NumberList.js +12 -0
  104. package/docs/src/components/htmlEditorControls/Strikethrough.js +12 -0
  105. package/docs/src/components/htmlEditorControls/TextBackgroundColor.js +97 -0
  106. package/docs/src/components/htmlEditorControls/TextColor.js +97 -0
  107. package/docs/src/components/htmlEditorControls/Underline.js +12 -0
  108. package/docs/src/components/htmlEditorControls/WordCount.js +18 -0
  109. package/docs/src/utils/formatCode.js +1 -0
  110. package/icons/arrow_drop_down.svg +1 -0
  111. package/icons/circle.svg +1 -0
  112. package/icons/code.svg +1 -0
  113. package/icons/code_blocks.svg +1 -0
  114. package/icons/copy.svg +1 -0
  115. package/icons/dot.svg +1 -0
  116. package/icons/format_align_center.svg +1 -0
  117. package/icons/format_align_justify.svg +1 -0
  118. package/icons/format_align_left.svg +1 -0
  119. package/icons/format_align_right.svg +1 -0
  120. package/icons/format_bold.svg +1 -0
  121. package/icons/format_clear.svg +1 -0
  122. package/icons/format_color_fill.svg +1 -0
  123. package/icons/format_color_text.svg +1 -0
  124. package/icons/format_h1.svg +1 -0
  125. package/icons/format_h2.svg +1 -0
  126. package/icons/format_h3.svg +1 -0
  127. package/icons/format_h4.svg +1 -0
  128. package/icons/format_h5.svg +1 -0
  129. package/icons/format_h6.svg +1 -0
  130. package/icons/format_italic.svg +1 -0
  131. package/icons/format_list_bulleted.svg +1 -0
  132. package/icons/format_list_numbered.svg +1 -0
  133. package/icons/format_paragraph.svg +1 -0
  134. package/icons/format_underlined.svg +1 -0
  135. package/icons/help.svg +1 -0
  136. package/icons/link.svg +1 -0
  137. package/icons/more_vert.svg +1 -0
  138. package/icons/strikethrough_s.svg +1 -0
  139. package/icons/table.svg +1 -0
  140. package/icons/wysiwyg.svg +1 -0
  141. package/package.json +6 -5
  142. package/scripts/getIcon.js +52 -0
  143. package/src/components/Dialog.js +25 -10
  144. package/src/components/HtmlEditor.js +1548 -0
  145. package/src/components/Icon.js +6 -3
  146. package/src/components/Main.js +22 -23
  147. package/src/components/ShadowComponent.js +0 -1
  148. package/src/components/SideMenu.js +10 -0
  149. package/src/components/SidePanel.js +78 -35
  150. package/src/components/htmlEditorControls/AlignCenter.js +65 -0
  151. package/src/components/htmlEditorControls/AlignJustify.js +65 -0
  152. package/src/components/htmlEditorControls/AlignLeft.js +65 -0
  153. package/src/components/htmlEditorControls/AlignRight.js +65 -0
  154. package/src/components/htmlEditorControls/Bold.js +65 -0
  155. package/src/components/htmlEditorControls/BulletList.js +65 -0
  156. package/src/components/htmlEditorControls/CharacterCount.js +75 -0
  157. package/src/components/htmlEditorControls/ClearFormatting.js +65 -0
  158. package/src/components/htmlEditorControls/CodeBlock.js +72 -0
  159. package/src/components/htmlEditorControls/ControlGroup.js +95 -0
  160. package/src/components/htmlEditorControls/ControlSpacer.js +23 -0
  161. package/src/components/htmlEditorControls/CreateLink.js +121 -0
  162. package/src/components/htmlEditorControls/DropdownControl.js +149 -0
  163. package/src/components/htmlEditorControls/FormatBlock.js +108 -0
  164. package/src/components/htmlEditorControls/HtmlEditorControl.js +126 -0
  165. package/src/components/htmlEditorControls/InlineCode.js +77 -0
  166. package/src/components/htmlEditorControls/InsertTable.js +171 -0
  167. package/src/components/htmlEditorControls/Italic.js +65 -0
  168. package/src/components/htmlEditorControls/Mode.js +66 -0
  169. package/src/components/htmlEditorControls/NumberList.js +65 -0
  170. package/src/components/htmlEditorControls/Strikethrough.js +65 -0
  171. package/src/components/htmlEditorControls/TextBackgroundColor.js +195 -0
  172. package/src/components/htmlEditorControls/TextColor.js +195 -0
  173. package/src/components/htmlEditorControls/Underline.js +65 -0
  174. package/src/components/htmlEditorControls/WordCount.js +76 -0
  175. package/src/utils/formatCode.js +87 -0
  176. package/tests/components/Main.browser-test.js +166 -105
  177. package/tests/components/SidePanel.browser-test.js +62 -8
  178. package/.github/workflows/publish-major.yml +0 -39
  179. package/.github/workflows/publish-minor.yml +0 -39
  180. package/.github/workflows/publish-patch.yml +0 -45
  181. package/docs/icons/arrow-back.svg +0 -1
  182. package/docs/icons/arrow-down-double.svg +0 -1
  183. package/docs/icons/arrow-down.svg +0 -1
  184. package/docs/icons/arrow-forward.svg +0 -1
  185. package/docs/icons/arrow-menu-close.svg +0 -1
  186. package/docs/icons/arrow-menu-open.svg +0 -1
  187. package/docs/icons/arrow-right.svg +0 -1
  188. package/docs/icons/arrow-up-double.svg +0 -1
  189. package/docs/icons/arrow-up.svg +0 -1
  190. package/docs/icons/chevron-left.svg +0 -1
  191. package/docs/icons/chevron-right.svg +0 -1
  192. package/docs/icons/chevron-up.svg +0 -1
@@ -150,6 +150,17 @@ export default class MyComponent extends HybridComponent {
150
150
 
151
151
  ## Development Workflow
152
152
 
153
+ ### Icons
154
+ - All icons are stored in the `icons/` directory as SVG files
155
+ - Icons should have no `width` or `height` attributes, and use `fill="currentColor"` for theming
156
+ - To download new Material Icons from Google's CDN:
157
+ ```bash
158
+ npm run geticon icon_name
159
+ ```
160
+ Example: `npm run geticon format_bold`
161
+ - The script automatically formats the icon with proper attributes
162
+ - Browse available icons at [Google Material Symbols](https://fonts.google.com/icons)
163
+
153
164
  ### Local Development Server
154
165
  - **DO NOT** start a development server - one is already running
155
166
  - Default port: **8083**
@@ -0,0 +1,66 @@
1
+ name: Publish to npmjs
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ workflow_dispatch:
8
+ inputs:
9
+ version-type:
10
+ description: 'Version bump type'
11
+ required: true
12
+ type: choice
13
+ options:
14
+ - patch
15
+ - minor
16
+ - major
17
+ default: patch
18
+
19
+ jobs:
20
+ publish:
21
+ runs-on: ubuntu-latest
22
+ permissions:
23
+ contents: write
24
+ id-token: write
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ with:
28
+ token: ${{ secrets.GITHUB_TOKEN }}
29
+
30
+ - uses: actions/setup-node@v4
31
+ with:
32
+ node-version: '20.x'
33
+
34
+ - run: npm ci
35
+
36
+ - name: Set version type
37
+ id: version
38
+ run: |
39
+ if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
40
+ echo "type=${{ inputs.version-type }}" >> $GITHUB_OUTPUT
41
+ else
42
+ echo "type=patch" >> $GITHUB_OUTPUT
43
+ fi
44
+
45
+ - name: Bump ${{ steps.version.outputs.type }} version
46
+ run: |
47
+ git config --global user.name "github-actions"
48
+ git config --global user.email "github-actions@github.com"
49
+ npm version ${{ steps.version.outputs.type }} --no-git-tag-version
50
+ git add package.json package-lock.json || true
51
+ git commit -m "ci: bump ${{ steps.version.outputs.type }} version [skip ci]" || echo "No changes to commit"
52
+ git push origin HEAD:main || echo "No changes to push"
53
+ env:
54
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
55
+
56
+ - name: Setup .npmrc for OIDC
57
+ run: |
58
+ cat << EOF > ~/.npmrc
59
+ //registry.npmjs.org/:_authToken=\${NPM_TOKEN}
60
+ registry=https://registry.npmjs.org/
61
+ always-auth=true
62
+ EOF
63
+
64
+ - run: npm publish --access public
65
+ env:
66
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
package/CONTRIBUTING.md CHANGED
@@ -147,3 +147,73 @@ export default class MyComponent extends HybridComponent {
147
147
  - Wrap dependent components inside a parent `ktf-test-framework` element. Children find it via `closest('ktf-test-framework')` and call its methods. The framework can query its subtree to orchestrate children.
148
148
 
149
149
  - Avoid `window` globals and global custom events for coordination. If broadcast is needed, scope events to the framework element; reserve window events for global, non-visual concerns (e.g., settings changes).
150
+
151
+ ## Development Workflow
152
+
153
+ ### NPM Scripts
154
+
155
+ #### Build
156
+ ```bash
157
+ npm run build
158
+ ```
159
+ Compiles and minifies source files from `src/` and copies all necessary files to the `docs/` directory for GitHub Pages deployment.
160
+
161
+ #### Documentation Server
162
+ ```bash
163
+ npm run docs
164
+ ```
165
+ Starts a local development server and opens the documentation site. Automatically runs the build script first.
166
+
167
+ **Variants:**
168
+ - `npm run docs:src` - Serves from `src/` instead of `docs/` for development
169
+ - `npm run docs:no-build` - Skips the build step and serves existing `docs/`
170
+
171
+ #### Testing
172
+ ```bash
173
+ npm test # Run all tests
174
+ npm run test:gui # Run tests with GUI interface
175
+ npm run test:browser # Run browser-only tests
176
+ npm run test:node # Run Node.js-only tests
177
+ ```
178
+
179
+ #### Update Kempo CSS
180
+ ```bash
181
+ npm run update-kempo-css
182
+ ```
183
+ Downloads the latest version of Kempo CSS from the CDN and updates local files.
184
+
185
+ ### Adding Icons
186
+
187
+ Use the `geticon` script to download icons from [Google Material Symbols](https://fonts.google.com/icons). The script automatically formats icons and triggers a build.
188
+
189
+ #### Basic Usage
190
+ ```bash
191
+ npm run geticon <icon_name>
192
+ ```
193
+
194
+ Example:
195
+ ```bash
196
+ npm run geticon format_bold
197
+ ```
198
+
199
+ This downloads the icon and saves it as `format_bold.svg` in the `icons/` directory.
200
+
201
+ #### Custom Naming
202
+
203
+ Rename icons when downloading by providing a second argument:
204
+ ```bash
205
+ npm run geticon <icon_name> <custom_name>
206
+ ```
207
+
208
+ Example - download `content_copy` but save as `copy.svg`:
209
+ ```bash
210
+ npm run geticon content_copy copy
211
+ ```
212
+
213
+ #### What the Script Does
214
+
215
+ - Downloads the specified icon from Google's Material Symbols CDN
216
+ - Removes `width` and `height` attributes
217
+ - Adds `fill="currentColor"` to all `<path>` elements
218
+ - Saves the formatted SVG to the `icons/` directory
219
+ - Automatically runs the build script to copy the icon to `docs/`
@@ -1,4 +1,4 @@
1
- import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";import{boolExists}from"../utils/propConverters.js";import"./Icon.js";import"./FocusCapture.js";const firstFocusable=e=>e.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')[0];export default class Dialog extends ShadowComponent{static properties={opened:{type:Boolean,reflect:!0,converter:boolExists},closeBtn:{type:Boolean,reflect:!0,attribute:"close-btn",converter:boolExists},overlayClose:{type:Boolean,reflect:!0,attribute:"overlay-close",converter:boolExists},disableKeyboardClose:{type:Boolean,reflect:!0,attribute:"disable-keyboard-close",converter:boolExists},confirmText:{type:String,reflect:!0,attribute:"confirm-text"},confirmClasses:{type:String,reflect:!0,attribute:"confirm-classes"},cancelText:{type:String,reflect:!0,attribute:"cancel-text"},cancelClasses:{type:String,reflect:!0,attribute:"cancel-classes"}};constructor(){super(),this.opened=!1,this.closeBtn=!0,this.overlayClose=!0,this.disableKeyboardClose=!1,this.confirmText="",this.confirmClasses="success ml",this.cancelText="",this.cancelClasses="",this.confirmAction=()=>{},this.cancelAction=()=>{},this.closeCallback=()=>{},this.previousFocus=null}handleClick=e=>{const{target:t}=e,o=t.id||t.closest("[id]")?.id;"overlay"===o&&this.overlayClose||"close"===o?this.close():"cancel"===o?(this.cancelAction(e),e.defaultPrevented||this.close()):"confirm"===o&&(this.confirmAction(e),e.defaultPrevented||this.close())};handleKeydown=e=>{this.disableKeyboardClose||27!==e.keyCode||this.close()};connectedCallback(){super.connectedCallback()}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("keydown",this.handleKeydown)}updated(e){super.updated(e),e.has("opened")&&this.dispatchEvent(new CustomEvent(this.opened?"opened":"close"))}open(){this.opened=!0,window.addEventListener("keydown",this.handleKeydown),document.body.classList.add("no-scroll");const e=this.updateComplete||this.requestUpdate();e&&e.then?e.then(()=>{const e=this.shadowRoot.querySelector("[autofocus]")||firstFocusable(this.shadowRoot);e&&e.focus()}):setTimeout(()=>{const e=this.shadowRoot.querySelector("[autofocus]")||firstFocusable(this.shadowRoot);e&&e.focus()},0)}close(){this.opened=!1,this.blur(),this.closeCallback(),window.removeEventListener("keydown",this.handleKeydown),document.body.classList.remove("no-scroll")}toggle(){this.opened?this.close():this.open()}focus(){const e=firstFocusable(this.shadowRoot);e&&(this.previousFocus=document.activeElement,e.focus())}blur(){this.previousFocus&&this.previousFocus.focus()}hasTitle(){return!!this.querySelector('[slot="title"]')}static styles=css`
1
+ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";import{boolExists}from"../utils/propConverters.js";import"./Icon.js";import"./FocusCapture.js";const firstFocusable=e=>e.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')[0];export default class Dialog extends ShadowComponent{static properties={opened:{type:Boolean,reflect:!0,converter:boolExists},closeBtn:{type:Boolean,reflect:!0,attribute:"close-btn",converter:boolExists},overlayClose:{type:Boolean,reflect:!0,attribute:"overlay-close",converter:boolExists},disableKeyboardClose:{type:Boolean,reflect:!0,attribute:"disable-keyboard-close",converter:boolExists},confirmText:{type:String,reflect:!0,attribute:"confirm-text"},confirmClasses:{type:String,reflect:!0,attribute:"confirm-classes"},cancelText:{type:String,reflect:!0,attribute:"cancel-text"},cancelClasses:{type:String,reflect:!0,attribute:"cancel-classes"}};constructor(){super(),this.opened=!1,this.closeBtn=!0,this.overlayClose=!0,this.disableKeyboardClose=!1,this.confirmText="",this.confirmClasses="success ml",this.cancelText="",this.cancelClasses="",this.confirmAction=()=>{},this.cancelAction=()=>{},this.closeCallback=()=>{},this.previousFocus=null}handleClick=e=>{const{target:t}=e,o=t.id||t.closest("[id]")?.id;"overlay"===o&&this.overlayClose||"close"===o?this.close():"cancel"===o&&(this.cancelAction(e),e.defaultPrevented||this.close())};handleSubmit=e=>{e.preventDefault(),this.confirmAction(e),e.keepDialogOpen||this.close()};handleSlotKeydown=e=>{if("Enter"===e.key&&this.confirmText){const t=e.target;if("INPUT"===t.tagName||"TEXTAREA"===t.tagName){e.preventDefault();const t=this.shadowRoot.querySelector("form");t&&t.requestSubmit()}}};handleKeydown=e=>{this.disableKeyboardClose||27!==e.keyCode||this.close()};connectedCallback(){super.connectedCallback()}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("keydown",this.handleKeydown)}updated(e){super.updated(e),e.has("opened")&&this.dispatchEvent(new CustomEvent(this.opened?"opened":"close"))}open(){this.opened=!0,window.addEventListener("keydown",this.handleKeydown),document.body.classList.add("no-scroll");const e=this.updateComplete||this.requestUpdate();e&&e.then?e.then(()=>{const e=this.shadowRoot.querySelector("[autofocus]")||firstFocusable(this.shadowRoot);e&&e.focus()}):setTimeout(()=>{const e=this.shadowRoot.querySelector("[autofocus]")||firstFocusable(this.shadowRoot);e&&e.focus()},0)}close(){this.opened=!1,this.blur(),this.closeCallback(),window.removeEventListener("keydown",this.handleKeydown),document.body.classList.remove("no-scroll")}toggle(){this.opened?this.close():this.open()}focus(){const e=firstFocusable(this.shadowRoot);e&&(this.previousFocus=document.activeElement,e.focus())}blur(){this.previousFocus&&this.previousFocus.focus()}hasTitle(){return!!this.querySelector('[slot="title"]')}static styles=css`
2
2
  :host {
3
3
  position: fixed;
4
4
  top: 0;
@@ -75,14 +75,15 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
75
75
  padding: var(--spacer_h);
76
76
  }
77
77
  `;render(){return html`
78
- <button id="overlay" aria-label="Close the Dialog" @click=${this.handleClick}></button>
78
+ <button id="overlay" type="button" aria-label="Close the Dialog" @click=${this.handleClick}></button>
79
79
  <div id="wrapper">
80
80
  <k-focus-capture>
81
- <div
81
+ <form
82
82
  id="dialog"
83
83
  role="dialog"
84
84
  aria-modal="true"
85
85
  aria-labelledby="title"
86
+ @submit=${this.handleSubmit}
86
87
  >
87
88
  <div
88
89
  id="header"
@@ -92,29 +93,29 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
92
93
  <slot name="title"></slot>
93
94
  </div>
94
95
  ${this.closeBtn?html`
95
- <button id="close" @click=${this.handleClick}>
96
+ <button id="close" type="button" @click=${this.handleClick}>
96
97
  <k-icon name="close"></k-icon>
97
98
  </button>
98
99
  `:""}
99
100
  </div>
100
101
  <div id="body">
101
- <slot></slot>
102
+ <slot @keydown=${this.handleSlotKeydown}></slot>
102
103
  </div>
103
104
  ${this.cancelText||this.confirmText?html`
104
105
  <div id="footer">
105
106
  ${this.cancelText?html`
106
- <button id="cancel" class="${this.cancelClasses}" @click=${this.handleClick}>
107
+ <button id="cancel" type="button" class="${this.cancelClasses}" @click=${this.handleClick}>
107
108
  ${this.cancelText}
108
109
  </button>
109
110
  `:""}
110
111
  ${this.confirmText?html`
111
- <button id="confirm" class="${this.confirmClasses}" @click=${this.handleClick}>
112
+ <button id="confirm" type="submit" class="${this.confirmClasses}">
112
113
  ${this.confirmText}
113
114
  </button>
114
115
  `:""}
115
116
  </div>
116
117
  `:""}
117
- </div>
118
+ </form>
118
119
  </k-focus-capture>
119
120
  </div>
120
121
  `}static create(e="",t={}){!1!==t.closeExisting&&document.querySelectorAll("k-dialog").forEach(e=>e.close());const{removeOnClose:o=!0,closeCallback:s=()=>{},title:i="",titleClasses:l="pyh px m0"}=t,a=new Dialog;if(Object.assign(a,{opened:!0,...t,closeCallback:(...e)=>{o&&a.remove(),s(...e)}}),i){const e=document.createElement("h5");e.slot="title",e.className=l,i instanceof HTMLElement?e.appendChild(i):e.innerHTML=i,a.appendChild(e)}if(e instanceof HTMLElement||e instanceof DocumentFragment)a.appendChild(e);else if(e){if(/<[^>]+>/.test(e))a.innerHTML+=e;else{const t=document.createElement("p");t.className="p",t.textContent=e,a.appendChild(t)}}return t.width&&a.style.setProperty("--width",t.width),t.minWidth&&a.style.setProperty("--min_width",t.minWidth),t.maxWidth&&a.style.setProperty("--max_width",t.maxWidth),t.height&&a.style.setProperty("--height",t.height),t.minHeight&&a.style.setProperty("--min_height",t.minHeight),t.maxHeight&&a.style.setProperty("--max_height",t.maxHeight),document.body.appendChild(a),a.open(),a}static confirm(e,t,o={}){return Dialog.create(e,{title:o.title||"Confirm",closeBtn:!1,overlayClose:!1,disableKeyboardClose:!0,confirmText:"Yes",confirmClasses:"success ml",confirmAction:()=>t(!0),cancelText:"No",cancelClasses:"danger",cancelAction:()=>t(!1),...o})}static alert(e,t=()=>{},o={}){return Dialog.create(e,{title:o.title||"Alert",closeCallback:t,cancelText:"Ok",...o})}static error(e,t=()=>{},o={}){return Dialog.create(e,{title:o.title||"Error",titleClasses:"pyh px m0 tc-danger",closeCallback:t,cancelText:"Ok",...o})}static success(e,t=()=>{},o={}){return Dialog.create(e,{title:o.title||"Success",titleClasses:"pyh px m0 tc-success",closeCallback:t,cancelText:"Ok",...o})}}customElements.define("k-dialog",Dialog);
@@ -0,0 +1,91 @@
1
+ import{html,css}from"../lit-all.min.js";import ShadowComponent from"./ShadowComponent.js";import formatCode from"../utils/formatCode.js";import debounce from"../utils/debounce.js";export default class HtmlEditor extends ShadowComponent{static formAssociated=!0;static properties={name:{type:String,reflect:!0},value:{type:String,reflect:!0},selection:{type:Object,state:!0},mode:{type:String,reflect:!0},hasTopToolbar:{type:Boolean,state:!0},hasBottomToolbar:{type:Boolean,state:!0}};constructor(){super(),this.internals=this.attachInternals(),this.name="",this.value="",this.selection=null,this.cursor=null,this.mode="visual",this.hasTopToolbar=!1,this.hasBottomToolbar=!1,this.skipSync=!1,this.savedSelection=null,this.debouncedDispatchChange=debounce(()=>{this.value=this.editorEl.innerHTML},300)}connectedCallback(){super.connectedCallback(),this.hasAttribute("value")&&(this.value=this.getAttribute("value")),this.slotObserver=new MutationObserver(()=>{this.updateToolbarVisibility()}),this.slotObserver.observe(this,{childList:!0,subtree:!0}),this.updateToolbarVisibility()}disconnectedCallback(){super.disconnectedCallback(),this.slotObserver?.disconnect(),this.boundUpdateSelection&&(this.shadowRoot.removeEventListener("selectionchange",this.boundUpdateSelection),this.editorEl&&(this.editorEl.removeEventListener("mouseup",this.boundUpdateSelection),this.editorEl.removeEventListener("keyup",this.boundUpdateSelection),this.editorEl.removeEventListener("focus",this.boundUpdateSelection)))}updateToolbarVisibility(){const e=Array.from(this.children).filter(e=>"toolbar-top"===e.getAttribute("slot")),t=Array.from(this.children).filter(e=>"toolbar-bottom"===e.getAttribute("slot"));this.hasTopToolbar=e.length>0,this.hasBottomToolbar=t.length>0}updated(e){super.updated(e),e.has("value")&&(this.syncContent(),this.updateFormValue(),this.dispatchEvent(new CustomEvent("change",{detail:{value:this.value},bubbles:!0}))),e.has("mode")&&(this.syncContent(),this.dispatchEvent(new CustomEvent("mode-changed",{detail:{mode:this.mode},bubbles:!0})))}firstUpdated(){this.editorEl=this.shadowRoot.querySelector(".editor"),this.textareaEl=this.shadowRoot.querySelector("textarea"),this.value&&this.syncContent(),this.boundUpdateSelection=this.updateSelection.bind(this),this.shadowRoot.addEventListener("selectionchange",this.boundUpdateSelection),this.editorEl.addEventListener("mouseup",this.boundUpdateSelection),this.editorEl.addEventListener("keyup",this.boundUpdateSelection),this.editorEl.addEventListener("focus",this.boundUpdateSelection),this.editorEl.addEventListener("keydown",this.handleTableNavigation),this.dispatchEvent(new CustomEvent("ready",{detail:{value:this.value},bubbles:!0}))}updateFormValue(){this.internals.setFormValue(this.getValue())}formResetCallback(){this.value=""}formStateRestoreCallback(e){this.value=e}handleTableNavigation=e=>{if("Tab"!==e.key&&"Enter"!==e.key)return;if("visual"!==this.mode)return;const t=this.shadowRoot.getSelection()||window.getSelection();if(!t.rangeCount)return;let n=t.getRangeAt(0).startContainer;n.nodeType===Node.TEXT_NODE&&(n=n.parentElement);const o=n.closest("td, th");if(o)if(e.preventDefault(),"Tab"===e.key){const n=o.parentElement,i=Array.from(n.cells),r=i.indexOf(o);if(e.shiftKey){let e=i[r-1];if(!e){const t=n.previousElementSibling;t&&(e=t.cells[t.cells.length-1])}if(e){const n=document.createRange();n.selectNodeContents(e),n.collapse(!1),t.removeAllRanges(),t.addRange(n),e.focus()}}else{let e=i[r+1];if(!e){const t=n.nextElementSibling;t&&(e=t.cells[0])}if(e){const n=document.createRange();n.selectNodeContents(e),n.collapse(!1),t.removeAllRanges(),t.addRange(n),e.focus()}}}else if("Enter"===e.key){const n=o.parentElement,i=Array.from(n.cells).indexOf(o);if(e.shiftKey){const e=n.previousElementSibling;if(e&&e.cells[i]){const n=e.cells[i],o=document.createRange();o.selectNodeContents(n),o.collapse(!1),t.removeAllRanges(),t.addRange(o),n.focus()}}else{const e=n.nextElementSibling;if(e&&e.cells[i]){const n=e.cells[i],o=document.createRange();o.selectNodeContents(n),o.collapse(!1),t.removeAllRanges(),t.addRange(o),n.focus()}else{const e=o.closest("table");if(e){const n=document.createElement("p");n.innerHTML="<br>",e.nextSibling?e.parentNode.insertBefore(n,e.nextSibling):e.parentNode.appendChild(n);const o=document.createRange();o.setStart(n,0),o.setEnd(n,0),t.removeAllRanges(),t.addRange(o),n.focus()}}}}};updateSelection=()=>{if("visual"!==this.mode)return void(this.selection=null);const e=this.shadowRoot.getSelection()||window.getSelection();if(!e.rangeCount)return void(this.selection=null);const t=e.getRangeAt(0),n=this.editorEl.contains(t.startContainer)||this.editorEl===t.startContainer,o=this.editorEl.contains(t.endContainer)||this.editorEl===t.endContainer;if(!n||!o)return void(this.selection=null);const i=t.toString();t.collapsed?(this.selection=null,this.cursor={container:t.startContainer,offset:t.startOffset}):(this.cursor=null,this.selection={startContainer:t.startContainer,startOffset:t.startOffset,endContainer:t.endContainer,endOffset:t.endOffset,text:i,collapsed:!1})};handleEditorInput=()=>{this.debouncedDispatchChange(),this.dispatchEvent(new CustomEvent("input",{detail:{value:this.editorEl.innerHTML}}))};handleTextareaInput=e=>{"code"===this.mode&&(this.value=e.target.value)};handleEditorPaste=e=>{e.preventDefault();const t=e.clipboardData.getData("text/plain");document.execCommand("insertText",!1,t)};handleEditorBlur=()=>{if("visual"===this.mode){const e=this.shadowRoot.getSelection()||window.getSelection();if(e.rangeCount>0){const t=e.getRangeAt(0);this.editorEl.contains(t.commonAncestorContainer)&&(this.savedSelection={startContainer:t.startContainer,startOffset:t.startOffset,endContainer:t.endContainer,endOffset:t.endOffset,collapsed:t.collapsed})}const t=this.cleanupHtml(this.editorEl.innerHTML);t!==this.value&&(this.value=t,this.syncContent())}};setMode(e){return["visual","code"].includes(e)&&("visual"===this.mode&&"code"===e&&(this.value=this.cleanupHtml(this.editorEl.innerHTML)),this.mode=e),this}toggleMode(){const e="visual"===this.mode?"code":"visual";return this.setMode(e),this}getValue(){if("visual"===this.mode){return(this.editorEl?this.editorEl.innerHTML:this.value).replace(/\u200B/g,"")}return this.value}setValue(e){return this.value=e,this.syncContent(),this}clear(){return this.value="",this.syncContent(),this}cleanupHtml(e){const t=document.createElement("div");t.innerHTML=e;const n=(e,t)=>{e.querySelectorAll(t).forEach(n=>{let o=n.parentElement;for(;o&&o!==e;){if(o.tagName.toLowerCase()===t){for(;n.firstChild;)n.parentNode.insertBefore(n.firstChild,n);n.parentNode.removeChild(n);break}o=o.parentElement}})};return["code","a","span","strong","em","b","i","u","s"].forEach(e=>n(t,e)),["pre","div","p","h1","h2","h3","h4","h5","h6","blockquote"].forEach(e=>n(t,e)),t.querySelectorAll("*").forEach(e=>{if(!e.hasAttribute("style"))return;const t=e.style,n=["inherit","rgba(0, 0, 0, 0)","transparent","initial","unset"];["font-size","font-family","background-color","line-height","font-weight","font-style"].forEach(e=>{const o=t.getPropertyValue(e);o&&n.some(e=>o.includes(e))&&t.removeProperty(e)}),0===t.length&&e.removeAttribute("style")}),(e=>{e.querySelectorAll("span").forEach(e=>{if(!e.className&&!e.id&&!e.hasAttribute("style")){for(;e.firstChild;)e.parentNode.insertBefore(e.firstChild,e);e.parentNode.removeChild(e)}})})(t),(e=>{const t=document.createTreeWalker(e,NodeFilter.SHOW_TEXT,null),n=[];for(;t.nextNode();)n.push(t.currentNode);n.forEach(e=>{e.textContent=e.textContent.replace(/\u00A0/g," ")})})(t),t.innerHTML}bold(){return this.execCommand("bold"),this}italic(){return this.execCommand("italic"),this}underline(){return this.execCommand("underline"),this}strikethrough(){return this.execCommand("strikeThrough"),this}orderedList(){return this.execCommand("insertOrderedList"),this}unorderedList(){return this.execCommand("insertUnorderedList"),this}alignLeft(){return this.execCommand("justifyLeft"),this}alignCenter(){return this.execCommand("justifyCenter"),this}alignRight(){return this.execCommand("justifyRight"),this}alignJustify(){return this.execCommand("justifyFull"),this}setTextColor(e){return this.execCommand("foreColor",e),this}removeTextColor(){return this.execCommand("removeFormat"),this}setTextBackgroundColor(e){return this.execCommand("backColor",e),this}removeTextBackgroundColor(){const e=this.shadowRoot.getSelection();if(!e||0===e.rangeCount)return this;const t=e.getRangeAt(0);if(t.collapsed)return this;const n=t.extractContents(),o=document.createTreeWalker(n,NodeFilter.SHOW_ELEMENT,null),i=e=>{e.style&&(e.style.backgroundColor="",e.style.cssText||e.removeAttribute("style"))};let r;for(;r=o.nextNode();)i(r);return i(n.firstChild),t.insertNode(n),this.editorEl.dispatchEvent(new Event("input",{bubbles:!0})),this}removeFormat(){if("visual"!==this.mode)return this;if(this.selection)return this.execCommand("removeFormat"),this;const e=this.shadowRoot.getSelection()||window.getSelection();if(!e.rangeCount)return this;let t=e.getRangeAt(0).startContainer;t.nodeType===Node.TEXT_NODE&&(t=t.parentElement);const n=t.closest(["B","I","U","S","STRONG","EM","CODE","MARK","SUB","SUP","SPAN"].map(e=>e.toLowerCase()).join(","));if(n&&this.editorEl.contains(n)){const t=n.textContent,o=document.createTextNode(t);n.replaceWith(o);const i=document.createRange();i.setStart(o,t.length),i.setEnd(o,t.length),e.removeAllRanges(),e.addRange(i),this.value=this.editorEl.innerHTML}return this}formatBlock(e){return this.execCommand("formatBlock",e),this}insertHTML(e){return this.execCommand("insertHTML",e),this}insertTable(e,t,n=!1,o=null){if("visual"!==this.mode)return this;const i=this.getValueWithSelectionMarkers();let r=i.html;const s=document.createElement("table");if(s.style.cssText="border-collapse: collapse; width: 100%;",n){const e=document.createElement("thead"),n=document.createElement("tr");for(let e=0;e<t;e++){const t=document.createElement("th");t.style.cssText="border: 1px solid var(--border-color, #ccc); padding: 0.5rem; text-align: left; background-color: var(--background-secondary, #f5f5f5);",o&&o[0]&&void 0!==o[0][e]?t.innerHTML=o[0][e]:t.innerHTML=`Header ${e+1}`,n.appendChild(t)}e.appendChild(n),s.appendChild(e)}const l=document.createElement("tbody"),a=n?1:0;for(let n=0;n<e;n++){const e=document.createElement("tr");for(let i=0;i<t;i++){const t=document.createElement("td");t.style.cssText="border: 1px solid var(--border-color, #ccc); padding: 0.5rem;",o&&o[n+a]&&void 0!==o[n+a][i]?t.innerHTML=o[n+a][i]:t.innerHTML="​",e.appendChild(t)}l.appendChild(e)}s.appendChild(l);const d=document.createElement("div");d.appendChild(s);const c=d.innerHTML,h=document.createElement("p");h.innerHTML="<br>";const u=c+h.outerHTML;i.hasCursor?r=r.replace("￰",u+"￰"):i.hasSelection?(r=r.replace(i.selectionStart,"").replace(i.selectionEnd,""),r+=u):r+=u;const m={hasCursor:i.hasCursor,hasSelection:!1,cursorMarker:"￰"};return this.setValueFromSelectionMarkers(r,m),this}insertElementAtCursor(e,t=!0){if("visual"!==this.mode)return this;const n=this.getValueWithSelectionMarkers();let o=n.html;t&&(e.textContent="￱"+(e.textContent||"")+"￲");const i=e.outerHTML;if(n.hasCursor)o=o.replace("￰",i);else{const t=["code","a","span","strong","em","b","i","u","s","mark","sub","sup"],n=e.tagName.toLowerCase();t.includes(n)?o=o+"<p>"+i+"</p>":o+=i}const r={hasCursor:!1,hasSelection:t,selectionStart:"￱",selectionEnd:"￲",selectedText:e.textContent||""};return this.setValueFromSelectionMarkers(o,r),this}replaceSelectionWithElement(e,t=!0){if("visual"!==this.mode)return this;const n=this.getValueWithSelectionMarkers();if(!n.hasSelection)return this;let o=n.html;t&&(e.textContent="￱"+e.textContent+"￲");const i=e.outerHTML,r="￱"+n.selectedText+"￲";o=o.replace(r,i);const s={hasCursor:!1,hasSelection:t,selectionStart:"￱",selectionEnd:"￲",selectedText:n.selectedText};return this.setValueFromSelectionMarkers(o,s),this}getSelection(){if("visual"!==this.mode)return null;this.editorEl.focus();const e=window.getSelection();if(!e.rangeCount)return null;const t=e.getRangeAt(0);return this.editorEl.contains(t.commonAncestorContainer)?{text:e.toString(),html:t.cloneContents().textContent?(new XMLSerializer).serializeToString(t.cloneContents()):"",range:t,selection:e}:null}getSelectedText(){const e=this.getSelection();return e?e.text:""}getSelectedHTML(){const e=this.getSelection();return e?e.html:""}setSelection(e,t,n,o){if("visual"!==this.mode)return this;this.editorEl.focus();const i=window.getSelection(),r=document.createRange();return r.setStart(e,t),r.setEnd(n,o),i.removeAllRanges(),i.addRange(r),this}selectAll(){return"visual"===this.mode?this.execCommand("selectAll"):this.textareaEl.select(),this}replaceSelection(e){if("visual"!==this.mode)return this;const t=this.getSelection();if(!t)return this;t.range.deleteContents();const n=t.range.createContextualFragment(e);return t.range.insertNode(n),this.value=this.editorEl.innerHTML,this}deleteSelection(){if("visual"===this.mode)this.execCommand("delete");else{const e=this.textareaEl.selectionStart,t=this.textareaEl.selectionEnd;this.textareaEl.value=this.textareaEl.value.substring(0,e)+this.textareaEl.value.substring(t),this.value=this.textareaEl.value}return this}getTextPosition(e,t){const n=document.createTreeWalker(e,NodeFilter.SHOW_TEXT,null);let o=0;for(;n.nextNode();){const e=n.currentNode;if(t.contains(e))return o;o+=e.textContent.length}return o}wrapSelection(e,t,n=null){if("visual"!==this.mode)return this;let o;if(n)o=n;else{const e=window.getSelection();if(!e.rangeCount||e.isCollapsed)return this;const t=e.getRangeAt(0);if(!this.editorEl.contains(t.commonAncestorContainer))return this;o=e.toString()}if(!o)return this;this.editorEl.focus();const i=e.match(/<([a-zA-Z]+)/);if(!i)return this;const r=i[1],s=document.createElement("div");s.innerHTML=this.getValue();const l=s.textContent.indexOf(o);if(-1===l)return this;const a=document.createTreeWalker(s,NodeFilter.SHOW_TEXT|NodeFilter.SHOW_ELEMENT,null);let d=0,c=null,h=0,u=null,m=0;const f=[];for(;a.nextNode();){const e=a.currentNode;if(e.nodeType===Node.ELEMENT_NODE){if(e.tagName.toLowerCase()===r){const t=this.getTextPosition(s,e),n=t+e.textContent.length,i=l+o.length;!(n<=l||t>=i)&&f.push(e)}continue}const t=e.textContent.length,n=d,i=d+t;if(!c&&i>l&&(c=e,h=l-n),i>=l+o.length){u=e,m=l+o.length-n;break}d+=t}if(f.length>0)f.forEach(e=>{const t=e.parentNode,n="pre"===e.tagName.toLowerCase(),o=[],i=e=>{o.push(e),Array.from(e.children).forEach(e=>i(e))};if(i(e),n){const n=document.createElement("p");for(;e.firstChild;)n.appendChild(e.firstChild);t.replaceChild(n,e)}else o.reverse().forEach(e=>{if(e.parentNode){for(;e.firstChild;)e.parentNode.insertBefore(e.firstChild,e);e.parentNode.removeChild(e)}})});else if(c&&u){const n=document.createRange();n.setStart(c,h),n.setEnd(u,m);const o=n.extractContents(),i=document.createElement("div");i.innerHTML=e+t;const l=i.firstChild;let a=l;for(;a.firstChild&&a.firstChild.nodeType===Node.ELEMENT_NODE;)a=a.firstChild;if(a.appendChild(o),n.insertNode(l),"pre"===r){let e=l.parentNode;for(;e&&e!==s;){if(e.tagName&&"p"===e.tagName.toLowerCase()){const t=[];let n=l.nextSibling;for(;n;){const e=n.nextSibling;t.push(n),n=e}if(e.parentNode.insertBefore(l,e.nextSibling),t.length>0&&t.some(e=>e.textContent.trim())){const e=document.createElement("p");t.forEach(t=>e.appendChild(t)),l.parentNode.insertBefore(e,l.nextSibling)}break}e=e.parentNode}}}return this.setValue(s.innerHTML),this.editorEl.focus(),setTimeout(()=>{const e=this.editorEl.textContent.indexOf(o);if(-1===e)return;const t=document.createTreeWalker(this.editorEl,NodeFilter.SHOW_TEXT,null);let n=0,i=null,r=0,s=null,l=0;for(;t.nextNode();){const a=t.currentNode,d=a.textContent.length,c=n,h=n+d;if(null===i&&h>e&&(i=a,r=e-c),h>=e+o.length){s=a,l=e+o.length-c;break}n+=d}if(i&&s){const e=document.createRange();e.setStart(i,r),e.setEnd(s,l);const t=window.getSelection();t.removeAllRanges(),t.addRange(e)}},0),this}insertAtCursor(e){return this.insertHTML(e),this}createLink(e){return this.execCommand("createLink",e),this}unlink(){return this.execCommand("unlink"),this}insertImage(e){return this.execCommand("insertImage",e),this}undo(){return this.execCommand("undo"),this}redo(){return this.execCommand("redo"),this}execCommand(e,t=null){"visual"===this.mode&&(this.editorEl.focus(),document.execCommand(e,!1,t),this.value=this.editorEl.innerHTML)}captureSelection(){if("visual"!==this.mode)return null;const e=window.getSelection();if(!e.rangeCount)return null;const t=e.getRangeAt(0);if(!this.editorEl.contains(t.commonAncestorContainer))return null;const n=t.toString();return t.collapsed?null:{startContainer:t.startContainer,startOffset:t.startOffset,endContainer:t.endContainer,endOffset:t.endOffset,text:n,collapsed:t.collapsed}}restoreSavedSelection(){if(!this.savedSelection)return!1;const e=this.shadowRoot.getSelection()||window.getSelection(),t=document.createRange();try{return t.setStart(this.savedSelection.startContainer,this.savedSelection.startOffset),t.setEnd(this.savedSelection.endContainer,this.savedSelection.endOffset),e.removeAllRanges(),e.addRange(t),!0}catch(e){return this.savedSelection=null,!1}}clearSavedSelection(){this.savedSelection=null}getValueWithSelectionMarkers(){if("visual"!==this.mode)return{html:this.value,hasCursor:!1,hasSelection:!1};const e=this.shadowRoot.getSelection()||window.getSelection();if(!e.rangeCount&&!this.restoreSavedSelection())return{html:this.editorEl.innerHTML,hasCursor:!1,hasSelection:!1};if(!e.rangeCount)return{html:this.editorEl.innerHTML,hasCursor:!1,hasSelection:!1};const t=e.getRangeAt(0);if(!this.editorEl.contains(t.commonAncestorContainer))return{html:this.editorEl.innerHTML,hasCursor:!1,hasSelection:!1};const n=t.toString();if(t.collapsed){const e=document.createTextNode("￰"),n=document.createRange();n.setStart(t.startContainer,t.startOffset),n.insertNode(e);const o=this.editorEl.innerHTML;return e.remove(),{html:o,hasCursor:!0,hasSelection:!1,cursorMarker:"￰",selectedText:""}}{const e=document.createTextNode("￱"),o=document.createTextNode("￲"),i=document.createRange();i.setStart(t.endContainer,t.endOffset),i.insertNode(o),i.setStart(t.startContainer,t.startOffset),i.insertNode(e);const r=this.editorEl.innerHTML;return e.remove(),o.remove(),{html:r,hasCursor:!1,hasSelection:!0,selectionStart:"￱",selectionEnd:"￲",selectedText:n}}}setValueFromSelectionMarkers(e,t){if("visual"!==this.mode)return this;const n=t.cursorMarker||"￰",o=t.selectionStart||"￱",i=t.selectionEnd||"￲";if(this.editorEl.innerHTML=e,t.hasCursor){const e=document.createTreeWalker(this.editorEl,NodeFilter.SHOW_TEXT,null);for(;e.nextNode();){const t=e.currentNode,o=t.textContent.indexOf(n);if(-1!==o){t.textContent=t.textContent.replace(n,"");const e=document.createRange();e.setStart(t,o),e.setEnd(t,o);const i=this.shadowRoot.getSelection()||window.getSelection();i.removeAllRanges(),i.addRange(e);break}}}else if(t.hasSelection){const e=document.createTreeWalker(this.editorEl,NodeFilter.SHOW_TEXT,null);let t=null,n=0,r=null,s=0;for(;e.nextNode();){const l=e.currentNode,a=l.textContent.indexOf(o);-1!==a&&(t=l,n=a,l.textContent=l.textContent.replace(o,""));const d=l.textContent.indexOf(i);if(-1!==d&&(r=l,s=d,l.textContent=l.textContent.replace(i,"")),t&&r)break}if(t&&r){const e=document.createRange();e.setStart(t,n),e.setEnd(r,s);const o=this.shadowRoot.getSelection()||window.getSelection();o.removeAllRanges(),o.addRange(e)}}return this.editorEl.focus(),this}syncContent(){if(this.editorEl&&this.textareaEl&&!this.skipSync)if("visual"===this.mode)this.editorEl.innerHTML!==this.value&&(this.editorEl.innerHTML=this.value);else{const e=formatCode(this.value);this.textareaEl.value!==e&&(this.textareaEl.value=e)}}static styles=css`
2
+ :host {
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: 0;
6
+ height: 300px;
7
+ }
8
+
9
+ .toolbar-top,
10
+ .toolbar-bottom {
11
+ display: flex;
12
+ flex-wrap: wrap;
13
+ align-items: center;
14
+ gap: 0;
15
+ background: var(--bg-secondary);
16
+ min-height: 40px;
17
+ }
18
+
19
+ .editor-container {
20
+ position: relative;
21
+ flex: 1;
22
+ min-height: 0;
23
+ overflow: hidden;
24
+ }
25
+
26
+ .editor,
27
+ textarea {
28
+ width: 100%;
29
+ height: 100%;
30
+ padding: 1rem;
31
+ border: 1px solid var(--border-color);
32
+ background: var(--bg-primary);
33
+ color: var(--text-primary);
34
+ font-family: inherit;
35
+ font-size: inherit;
36
+ line-height: 1.5;
37
+ overflow: auto;
38
+ box-sizing: border-box;
39
+ }
40
+
41
+ .editor {
42
+ outline: none;
43
+ }
44
+
45
+ .editor:focus {
46
+ border-color: var(--primary-color);
47
+ }
48
+
49
+ textarea {
50
+ resize: none;
51
+ font-family: monospace;
52
+ }
53
+
54
+ textarea:focus {
55
+ outline: none;
56
+ border-color: var(--primary-color);
57
+ }
58
+
59
+ .editor[hidden],
60
+ textarea[hidden] {
61
+ display: none;
62
+ }
63
+ `;render(){return html`
64
+ ${this.hasTopToolbar?html`
65
+ <div class="toolbar-top bb">
66
+ <slot name="toolbar-top"></slot>
67
+ </div>
68
+ `:""}
69
+
70
+ <div class="editor-container">
71
+ <div
72
+ class="editor"
73
+ contenteditable="true"
74
+ ?hidden=${"visual"!==this.mode}
75
+ @input=${this.handleEditorInput}
76
+ @paste=${this.handleEditorPaste}
77
+ @blur=${this.handleEditorBlur}
78
+ ></div>
79
+
80
+ <textarea
81
+ ?hidden=${"code"!==this.mode}
82
+ @input=${this.handleTextareaInput}
83
+ ></textarea>
84
+ </div>
85
+
86
+ ${this.hasBottomToolbar?html`
87
+ <div class="toolbar-bottom bt">
88
+ <slot name="toolbar-bottom"></slot>
89
+ </div>
90
+ `:""}
91
+ `}}customElements.define("k-html-editor",HtmlEditor);
@@ -1,4 +1,4 @@
1
- import ShadowComponent from"./ShadowComponent.js";import{html,css,unsafeHTML}from"../lit-all.min.js";const cache={},getIconByPath=async t=>(cache[t]||(cache[t]=new Promise(async(n,e)=>{const i=(new AbortController).signal;try{const e=await fetch(t,{signal:i});200===e.status?n(await e.text()):404===e.status&&n(null)}catch(t){"AbortError"!==t.name&&e(t)}}).catch(()=>null)),await cache[t]),getIconByName=t=>{const n=async n=>{const e=n.endsWith("/")?n.slice(0,-1):n;return getIconByPath(`${e}/${t}.svg`)};return new Promise(async(t,e)=>{let i;const o=window.kempo?.pathsToIcons||["/icons","../../icons","https://cdn.jsdelivr.net/npm/kempo-ui@0.0.32/icons/"];for(let t=0;t<o.length&&!i;t++)try{i=await n(o[t])}catch(t){}t(i||null)})};export default class Icon extends ShadowComponent{static properties={src:{type:String,reflect:!0},name:{type:String,reflect:!0},rotation:{type:String,reflect:!0},direction:{type:String,reflect:!0},animation:{type:String,reflect:!0},iconContent:{type:String}};constructor(t=""){super(),this.src="",this.name=t,this.rotation="",this.direction="",this.animation="",this.iconContent=""}updated(t){super.updated(),(t.has("src")||t.has("name"))&&this.loadIcon()}async loadIcon(){let t;if(this.src?t=await getIconByPath(this.src):this.name&&(t=await getIconByName(this.name)),t)this.iconContent=this.fixSVG(t);else{const t=this.innerHTML.trim();this.iconContent=t?this.fixSVG(t):window.kempo?.fallbackIcon||'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M480-79q-16 0-30.5-6T423-102L102-423q-11-12-17-26.5T79-480q0-16 6-31t17-26l321-321q12-12 26.5-17.5T480-881q16 0 31 5.5t26 17.5l321 321q12 11 17.5 26t5.5 31q0 16-5.5 30.5T858-423L537-102q-11 11-26 17t-31 6Zm0-80 321-321-321-321-321 321 321 321Zm-40-281h80v-240h-80v240Zm40 120q17 0 28.5-11.5T520-360q0-17-11.5-28.5T480-400q-17 0-28.5 11.5T440-360q0 17 11.5 28.5T480-320Zm0-160Z"/></svg>'}}fixSVG(t){if(!t)return t;const n=(new DOMParser).parseFromString(t,"image/svg+xml").querySelector("svg");return n?(n.removeAttribute("width"),n.removeAttribute("height"),n.querySelectorAll("path, rect, circle").forEach(t=>{t.setAttribute("fill","currentColor")}),(new XMLSerializer).serializeToString(n)):t}getRotationDegrees(){if(this.rotation)return this.rotation;return{down:"90",left:"180",up:"270"}[this.direction]||"0"}static styles=css`
1
+ import ShadowComponent from"./ShadowComponent.js";import{html,css,unsafeHTML}from"../lit-all.min.js";const cache={},getIconByPath=async t=>(cache[t]||(cache[t]=new Promise(async(e,n)=>{const i=(new AbortController).signal;try{const n=await fetch(t,{signal:i});200===n.status?e(await n.text()):e(null)}catch(t){"AbortError"!==t.name&&n(t)}}).catch(()=>null)),await cache[t]),getIconByName=t=>{const e=async e=>{const n=e.endsWith("/")?e.slice(0,-1):e;return getIconByPath(`${n}/${t}.svg`)};return new Promise(async(t,n)=>{let i;const o=window.kempo?.pathsToIcons||["./icons","/icons","../icons","../../icons"];for(let t=0;t<o.length&&!i;t++)try{i=await e(o[t])}catch(t){}t(i||null)})};export default class Icon extends ShadowComponent{static properties={src:{type:String,reflect:!0},name:{type:String,reflect:!0},rotation:{type:String,reflect:!0},direction:{type:String,reflect:!0},animation:{type:String,reflect:!0},iconContent:{type:String}};constructor(t=""){super(),this.src="",this.name=t,this.rotation="",this.direction="",this.animation="",this.iconContent=""}updated(t){super.updated(),(t.has("src")||t.has("name"))&&this.loadIcon()}async loadIcon(){let t;if(this.src?t=await getIconByPath(this.src):this.name&&(t=await getIconByName(this.name)),t)this.iconContent=this.fixSVG(t),this.requestUpdate();else{const t=this.innerHTML.trim();t?(this.iconContent=this.fixSVG(t),this.requestUpdate()):(this.iconContent=window.kempo?.fallbackIcon||'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path fill="currentColor" d="M480-79q-16 0-30.5-6T423-102L102-423q-11-12-17-26.5T79-480q0-16 6-31t17-26l321-321q12-12 26.5-17.5T480-881q16 0 31 5.5t26 17.5l321 321q12 11 17.5 26t5.5 31q0 16-5.5 30.5T858-423L537-102q-11 11-26 17t-31 6Zm0-80 321-321-321-321-321 321 321 321Zm-40-281h80v-240h-80v240Zm40 120q17 0 28.5-11.5T520-360q0-17-11.5-28.5T480-400q-17 0-28.5 11.5T440-360q0 17 11.5 28.5T480-320Zm0-160Z"/></svg>',this.requestUpdate())}}fixSVG(t){if(!t)return t;const e=(new DOMParser).parseFromString(t,"image/svg+xml").querySelector("svg");return e?(e.removeAttribute("width"),e.removeAttribute("height"),e.querySelectorAll("path, rect, circle").forEach(t=>{t.setAttribute("fill","currentColor")}),(new XMLSerializer).serializeToString(e)):t}getRotationDegrees(){if(this.rotation)return this.rotation;return{down:"90",left:"180",up:"270"}[this.direction]||"0"}static styles=css`
2
2
  :host {
3
3
  display: inline-block;
4
4
  vertical-align: bottom;
@@ -39,4 +39,4 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css,unsafeHTML}fro
39
39
  0%, 100% { transform: scale(1); }
40
40
  50% { transform: scale(1.2); }
41
41
  }
42
- `;render(){const t=this.getRotationDegrees(),n="0"!==t?`--icon-rotation: ${t}deg;`:"";return this.iconContent?html`<div style="${n}">${unsafeHTML(this.iconContent)}</div>`:html`<div style="${n}"><slot></slot></div>`}}window.customElements.define("k-icon",Icon);
42
+ `;render(){const t=this.getRotationDegrees(),e="0"!==t?`--icon-rotation: ${t}deg;`:"";return this.iconContent?html`<div style="${e}">${unsafeHTML(this.iconContent)}</div>`:html`<div style="${e}"><slot></slot></div>`}}window.customElements.define("k-icon",Icon);
@@ -1 +1 @@
1
- import{LitElement}from"../lit-all.min.js";export default class ShadowComponent extends LitElement{#e;connectedCallback(){super.connectedCallback(),this.#e=new MutationObserver(()=>{console.log("MutationObserver fired for",this.constructor.name),this.childrenUpdated(),this.requestUpdate()}),this.#e.observe(this,{childList:!0})}disconnectedCallback(){super.disconnectedCallback(),this.#e?.disconnect()}childrenUpdated(){}createRenderRoot(){const e=this.attachShadow({mode:"open"}),t=document.createElement("link");t.rel="stylesheet",t.href=window.kempo?.pathToStylesheet||"https://cdn.jsdelivr.net/npm/kempo-css@1.3.2/dist/kempo.min.css",e.appendChild(t);const n=this.constructor.styles;if(n){const t=document.createElement("style");Array.isArray(n)?t.textContent=n.map(e=>e.cssText||e).join("\n"):t.textContent=n.cssText||n,e.appendChild(t)}const s=document.createElement("div");return s.style.display="contents",e.appendChild(s),s}}
1
+ import{LitElement}from"../lit-all.min.js";export default class ShadowComponent extends LitElement{#e;connectedCallback(){super.connectedCallback(),this.#e=new MutationObserver(()=>{this.childrenUpdated(),this.requestUpdate()}),this.#e.observe(this,{childList:!0})}disconnectedCallback(){super.disconnectedCallback(),this.#e?.disconnect()}childrenUpdated(){}createRenderRoot(){const e=this.attachShadow({mode:"open"}),t=document.createElement("link");t.rel="stylesheet",t.href=window.kempo?.pathToStylesheet||"https://cdn.jsdelivr.net/npm/kempo-css@1.3.2/dist/kempo.min.css",e.appendChild(t);const n=this.constructor.styles;if(n){const t=document.createElement("style");Array.isArray(n)?t.textContent=n.map(e=>e.cssText||e).join("\n"):t.textContent=n.cssText||n,e.appendChild(t)}const s=document.createElement("div");return s.style.display="contents",e.appendChild(s),s}}
@@ -1,4 +1,4 @@
1
- import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";import{boolTrueFalse}from"../utils/propConverters.js";import"./FocusCapture.js";import"./Icon.js";export default class SideMenu extends ShadowComponent{static properties={opened:{type:Boolean,reflect:!0},overlayClose:{type:Boolean,reflect:!0,attribute:"overlay-close",converter:boolTrueFalse},side:{type:String,reflect:!0}};constructor(){super(),this.opened=!1,this.overlayClose=!0,this.side="left"}overlayClick=()=>{this.overlayClose&&this.close()};updated(e){super.updated(e),e.has("opened")&&(this.opened?(document.body.classList.add("no-scroll"),this.dispatchEvent(new CustomEvent("change",{detail:"open"})),this.dispatchEvent(new CustomEvent("open"))):(document.body.classList.remove("no-scroll"),this.dispatchEvent(new CustomEvent("change",{detail:"close"})),this.dispatchEvent(new CustomEvent("close"))))}open(){this.opened=!0}close(){this.opened=!1}toggle(){this.opened?this.close():this.open(),this.dispatchEvent(new CustomEvent("toggle"))}render(){return html`
1
+ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";import{boolTrueFalse}from"../utils/propConverters.js";import"./FocusCapture.js";import"./Icon.js";export default class SideMenu extends ShadowComponent{static properties={opened:{type:Boolean,reflect:!0},overlayClose:{type:Boolean,reflect:!0,attribute:"overlay-close",converter:boolTrueFalse},escClose:{type:Boolean,reflect:!0,attribute:"esc-close",converter:boolTrueFalse},side:{type:String,reflect:!0}};constructor(){super(),this.opened=!1,this.overlayClose=!0,this.escClose=!0,this.side="left"}overlayClick=()=>{this.overlayClose&&this.close()};handleKeyDown=e=>{this.escClose&&"Escape"===e.key&&this.close()};updated(e){super.updated(e),e.has("opened")&&(this.opened?(document.body.classList.add("no-scroll"),document.addEventListener("keydown",this.handleKeyDown),this.dispatchEvent(new CustomEvent("change",{detail:"open"})),this.dispatchEvent(new CustomEvent("open"))):(document.body.classList.remove("no-scroll"),document.removeEventListener("keydown",this.handleKeyDown),this.dispatchEvent(new CustomEvent("change",{detail:"close"})),this.dispatchEvent(new CustomEvent("close"))))}open(){this.opened=!0}close(){this.opened=!1}toggle(){this.opened?this.close():this.open(),this.dispatchEvent(new CustomEvent("toggle"))}render(){return html`
2
2
  <k-focus-capture>
3
3
  <div id="container">
4
4
  <button id="overlay" @click=${this.overlayClick}>
@@ -1,4 +1,4 @@
1
- import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-all.min.js";import"./Icon.js";class SidePanel extends ShadowComponent{static properties={collapsed:{type:Boolean,reflect:!0},side:{type:String,reflect:!0}};constructor(){super(),this.collapsed=!1,this.side="left"}toggleClick=()=>this.toggle();updated(e){if(super.updated(e),e.has("collapsed")){const e=this.collapsed?"collapse":"expand";this.dispatchEvent(new CustomEvent(e)),this.dispatchEvent(new CustomEvent("change",{detail:e})),window.dispatchEvent(new CustomEvent("side-panel-change",{detail:{collapsed:this.collapsed,width:this.collapsed?"3.5rem":"16rem",side:this.side}}))}}expand=()=>this.collapsed=!1;collapse=()=>this.collapsed=!0;toggle(){this.collapsed=!this.collapsed,this.dispatchEvent(new CustomEvent("toggle"))}render(){return html`
1
+ import ShadowComponent from"./ShadowComponent.js";import{html,css,nothing}from"../lit-all.min.js";import"./Icon.js";class SidePanel extends ShadowComponent{static properties={collapsed:{type:Boolean,reflect:!0},side:{type:String,reflect:!0}};constructor(){super(),this.collapsed=!1,this.side="left"}toggleClick=()=>this.toggle();updated(e){if(super.updated(e),e.has("collapsed")){const e=this.collapsed?"collapse":"expand";this.dispatchEvent(new CustomEvent(e)),this.dispatchEvent(new CustomEvent("change",{detail:e})),window.dispatchEvent(new CustomEvent("side-panel-change",{detail:{collapsed:this.collapsed,width:this.collapsed?"3.5rem":"16rem",side:this.side}}))}}expand=()=>this.collapsed=!1;collapse=()=>this.collapsed=!0;toggle(){this.collapsed=!this.collapsed,this.dispatchEvent(new CustomEvent("toggle"))}render(){return html`
2
2
  <div id="header">
3
3
  <slot name="logo"></slot>
4
4
  <button id="toggle" @click=${this.toggleClick} aria-label="${this.collapsed?"Expand panel":"Collapse panel"}">
@@ -12,7 +12,6 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
12
12
  :host {
13
13
  --bg: var(--c_bg);
14
14
  --width-expanded: 16rem;
15
- --width-collapsed: 3.5rem;
16
15
  --transition-duration: var(--animation_ms, 256ms);
17
16
  display: block;
18
17
  position: fixed;
@@ -26,7 +25,7 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
26
25
  z-index: 1000;
27
26
  }
28
27
  :host([collapsed]) {
29
- width: var(--width-collapsed);
28
+ width: auto;
30
29
  }
31
30
  :host([side="right"]) {
32
31
  left: auto;
@@ -52,9 +51,7 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
52
51
  transition: opacity var(--transition-duration);
53
52
  }
54
53
  :host([collapsed]) ::slotted([slot="logo"]) {
55
- opacity: 0;
56
- width: 0;
57
- overflow: hidden;
54
+ display: none;
58
55
  }
59
56
  #toggle {
60
57
  flex-shrink: 0;
@@ -79,17 +76,24 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
79
76
  display: flex;
80
77
  flex-direction: column;
81
78
  }
82
- `}class SidePanelItem extends ShadowComponent{static properties={icon:{type:String},href:{type:String},active:{type:Boolean,reflect:!0}};constructor(){super(),this.icon="",this.href="#",this.active=!1,this.collapsed=!1}connectedCallback(){super.connectedCallback(),this.panel=this.closest("k-side-panel"),this.panel&&(this.collapsed=this.panel.collapsed,this.panel.addEventListener("collapse",this.handleCollapse),this.panel.addEventListener("expand",this.handleExpand))}disconnectedCallback(){super.disconnectedCallback(),this.panel&&(this.panel.removeEventListener("collapse",this.handleCollapse),this.panel.removeEventListener("expand",this.handleExpand))}handleCollapse=()=>{this.collapsed=!0,this.requestUpdate()};handleExpand=()=>{this.collapsed=!1,this.requestUpdate()};render(){return html`
83
- <a href="${this.href}" class="item ${this.active?"active bg-primary":""}">
84
- ${this.icon?html`<k-icon name="${this.icon}"></k-icon>`:""}
85
- <span class="label ${this.collapsed?"hidden":""}">
86
- <slot></slot>
87
- </span>
79
+ :host([collapsed]) #content {
80
+ align-items: flex-start;
81
+ }
82
+ :host(:not([collapsed])) #content {
83
+ /* scrollbar-gutter: stable; */
84
+ }
85
+ `}class SidePanelItem extends ShadowComponent{static properties={icon:{type:String},href:{type:String},active:{type:Boolean,reflect:!0},collapsed:{type:Boolean,reflect:!0},"no-expand":{type:Boolean,attribute:"no-expand"},"hide-when-collapsed":{type:Boolean,attribute:"hide-when-collapsed"}};constructor(){super(),this.icon="",this.href="#",this.active=!1,this.collapsed=!1,this["no-expand"]=!1,this["hide-when-collapsed"]=!1}connectedCallback(){super.connectedCallback(),this.panel=this.closest("k-side-panel"),this.panel&&(this.collapsed=this.panel.collapsed,this.panel.addEventListener("collapse",this.handleCollapse),this.panel.addEventListener("expand",this.handleExpand))}disconnectedCallback(){super.disconnectedCallback(),this.panel&&(this.panel.removeEventListener("collapse",this.handleCollapse),this.panel.removeEventListener("expand",this.handleExpand))}handleCollapse=()=>{this.collapsed=!0,this.requestUpdate()};handleExpand=()=>{this.collapsed=!1,this.requestUpdate()};handleClick=e=>{this.collapsed&&!this["no-expand"]&&this.panel&&(e.preventDefault(),this.panel.expand())};render(){return html`
86
+ <a href="${this.href}" class="item ${this.active?"active bg-primary":""}" @click=${this.handleClick}>
87
+ ${this.icon?html`<k-icon name="${this.icon}"></k-icon>`:this.collapsed?html`<k-icon name="dot"></k-icon>`:nothing}
88
+ ${this.collapsed?nothing:html`<span class="label"><slot></slot></span>`}
88
89
  </a>
89
90
  `}static styles=css`
90
91
  :host {
91
92
  display: block;
92
93
  }
94
+ :host([collapsed][hide-when-collapsed]) {
95
+ display: none;
96
+ }
93
97
  .item {
94
98
  display: flex;
95
99
  align-items: center;
@@ -102,6 +106,8 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
102
106
  transition: background var(--animation_ms), color var(--animation_ms);
103
107
  white-space: nowrap;
104
108
  }
109
+ :host([collapsed]) .item {
110
+ }
105
111
  .item:hover {
106
112
  background: var(--c_bg_hover);
107
113
  }
@@ -119,18 +125,16 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
119
125
  min-width: 0;
120
126
  overflow: hidden;
121
127
  text-overflow: ellipsis;
122
- opacity: 1;
123
- transition: opacity var(--animation_ms);
124
128
  }
125
- .label.hidden {
126
- opacity: 0;
127
- width: 0;
128
- }
129
- `}class SidePanelLabel extends ShadowComponent{constructor(){super(),this.collapsed=!1}connectedCallback(){super.connectedCallback(),this.panel=this.closest("k-side-panel"),this.panel&&(this.collapsed=this.panel.collapsed,this.panel.addEventListener("collapse",this.handleCollapse),this.panel.addEventListener("expand",this.handleExpand))}disconnectedCallback(){super.disconnectedCallback(),this.panel&&(this.panel.removeEventListener("collapse",this.handleCollapse),this.panel.removeEventListener("expand",this.handleExpand))}handleCollapse=()=>{this.collapsed=!0,this.requestUpdate()};handleExpand=()=>{this.collapsed=!1,this.requestUpdate()};render(){return this.collapsed?html`<hr>`:html`<div class="label"><slot></slot></div>`}static styles=css`
129
+ `}class SidePanelLabel extends ShadowComponent{static properties={collapsed:{type:Boolean,reflect:!0}};constructor(){super(),this.collapsed=!1}connectedCallback(){super.connectedCallback(),this.panel=this.closest("k-side-panel"),this.panel&&(this.collapsed=this.panel.collapsed,this.panel.addEventListener("collapse",this.handleCollapse),this.panel.addEventListener("expand",this.handleExpand))}disconnectedCallback(){super.disconnectedCallback(),this.panel&&(this.panel.removeEventListener("collapse",this.handleCollapse),this.panel.removeEventListener("expand",this.handleExpand))}handleCollapse=()=>{this.collapsed=!0,this.requestUpdate()};handleExpand=()=>{this.collapsed=!1,this.requestUpdate()};render(){return this.collapsed?html`<hr>`:html`<div class="label"><slot></slot></div>`}static styles=css`
130
130
  :host {
131
131
  display: block;
132
132
  margin: var(--spacer_h) 0;
133
133
  }
134
+ :host([collapsed]) {
135
+ margin: 0 var(--spacer_h);
136
+ align-self: stretch;
137
+ }
134
138
  .label {
135
139
  padding: 0 var(--spacer);
136
140
  font-size: 0.75rem;
@@ -144,12 +148,15 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
144
148
  border-top: 1px solid var(--c_border);
145
149
  margin: var(--spacer_h) var(--spacer);
146
150
  }
147
- `}class SidePanelMenu extends ShadowComponent{static properties={icon:{type:String},label:{type:String},open:{type:Boolean,reflect:!0}};constructor(){super(),this.icon="",this.label="",this.open=!1,this.collapsed=!1}connectedCallback(){super.connectedCallback(),this.panel=this.closest("k-side-panel"),this.panel&&(this.collapsed=this.panel.collapsed,this.panel.addEventListener("collapse",this.handleCollapse),this.panel.addEventListener("expand",this.handleExpand))}disconnectedCallback(){super.disconnectedCallback(),this.panel&&(this.panel.removeEventListener("collapse",this.handleCollapse),this.panel.removeEventListener("expand",this.handleExpand))}handleCollapse=()=>{this.collapsed=!0,this.open=!1,this.requestUpdate()};handleExpand=()=>{this.collapsed=!1,this.requestUpdate()};toggleMenu=()=>{this.collapsed||(this.open=!this.open)};render(){return html`
151
+ :host([collapsed]) hr {
152
+ margin: var(--spacer_h) 0;
153
+ }
154
+ `}class SidePanelMenu extends ShadowComponent{static properties={icon:{type:String},label:{type:String},open:{type:Boolean,reflect:!0},collapsed:{type:Boolean,reflect:!0},"no-expand":{type:Boolean,attribute:"no-expand"},"hide-when-collapsed":{type:Boolean,attribute:"hide-when-collapsed"}};constructor(){super(),this.icon="",this.label="",this.open=!1,this.collapsed=!1,this["no-expand"]=!1,this["hide-when-collapsed"]=!1}connectedCallback(){super.connectedCallback(),this.panel=this.closest("k-side-panel"),this.panel&&(this.collapsed=this.panel.collapsed,this.panel.addEventListener("collapse",this.handleCollapse),this.panel.addEventListener("expand",this.handleExpand))}disconnectedCallback(){super.disconnectedCallback(),this.panel&&(this.panel.removeEventListener("collapse",this.handleCollapse),this.panel.removeEventListener("expand",this.handleExpand))}handleCollapse=()=>{this.collapsed=!0,this.open=!1,this.requestUpdate()};handleExpand=()=>{this.collapsed=!1,this.requestUpdate()};toggleMenu=()=>{this.collapsed&&!this["no-expand"]&&this.panel?this.panel.expand():this.collapsed||(this.open=!this.open)};render(){return html`
148
155
  <div class="menu-container">
149
- <button class="menu-header ${this.open?"open":""}" @click=${this.toggleMenu}>
150
- ${this.icon?html`<k-icon name="${this.icon}"></k-icon>`:""}
151
- <span class="label ${this.collapsed?"hidden":""}">${this.label}</span>
152
- ${this.collapsed?"":html`<k-icon class="chevron" name="chevron" direction="${this.open?"down":"right"}"></k-icon>`}
156
+ <button class="no-btn menu-header ${this.open?"open":""}" @click=${this.toggleMenu}>
157
+ ${this.icon?html`<k-icon name="${this.icon}"></k-icon>`:this.collapsed?html`<k-icon name="dot"></k-icon>`:nothing}
158
+ ${this.collapsed?nothing:html`<span class="label">${this.label}</span>`}
159
+ ${this.collapsed?nothing:html`<k-icon class="chevron" name="chevron" direction="${this.open?"down":"right"}"></k-icon>`}
153
160
  </button>
154
161
  <div class="menu-content ${this.open&&!this.collapsed?"open":""}">
155
162
  <slot></slot>
@@ -159,6 +166,9 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
159
166
  :host {
160
167
  display: block;
161
168
  }
169
+ :host([collapsed][hide-when-collapsed]) {
170
+ display: none;
171
+ }
162
172
  .menu-container {
163
173
  margin: 0;
164
174
  }
@@ -181,6 +191,9 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
181
191
  white-space: nowrap;
182
192
  transition: background var(--animation_ms);
183
193
  }
194
+ :host([collapsed]) .menu-header {
195
+ width: auto;
196
+ }
184
197
  .menu-header:hover {
185
198
  background: var(--c_bg_hover);
186
199
  }
@@ -192,12 +205,6 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
192
205
  min-width: 0;
193
206
  overflow: hidden;
194
207
  text-overflow: ellipsis;
195
- opacity: 1;
196
- transition: opacity var(--animation_ms);
197
- }
198
- .label.hidden {
199
- opacity: 0;
200
- width: 0;
201
208
  }
202
209
  .chevron {
203
210
  transition: transform var(--animation_ms);
@@ -208,6 +215,9 @@ import ShadowComponent from"./ShadowComponent.js";import{html,css}from"../lit-al
208
215
  transition: max-height var(--animation_ms);
209
216
  padding-left: calc(var(--spacer_h) * 2);
210
217
  }
218
+ :host([collapsed]) .menu-content {
219
+ padding-left: 0;
220
+ }
211
221
  .menu-content.open {
212
222
  max-height: 500px;
213
223
  }
@@ -0,0 +1,12 @@
1
+ import HtmlEditorControl from"./HtmlEditorControl.js";import{html,css}from"../../lit-all.min.js";import"../Icon.js";export default class AlignCenter extends HtmlEditorControl{static properties={editorMode:{type:String,state:!0}};static styles=[HtmlEditorControl.styles,css`
2
+ :host {
3
+ display: inline-flex;
4
+ }
5
+ `];connectedCallback(){super.connectedCallback(),this.updateEditorMode(),this.editor?.addEventListener("mode-changed",()=>this.updateEditorMode())}handleClick=()=>{this.editor&&this.editor.alignCenter()};updateEditorMode(){this.editor&&(this.editorMode=this.editor.mode)}render(){return this.hidden="code"===this.editorMode,html`
6
+ <button class="${this.buttonClasses}" @click="${this.handleClick}">
7
+ <slot name="icon">
8
+ <k-icon name="format_align_center"></k-icon>
9
+ </slot>
10
+ <slot></slot>
11
+ </button>
12
+ `}}customElements.define("k-hec-align-center",AlignCenter);
@@ -0,0 +1,12 @@
1
+ import HtmlEditorControl from"./HtmlEditorControl.js";import{html,css}from"../../lit-all.min.js";import"../Icon.js";export default class AlignJustify extends HtmlEditorControl{static properties={editorMode:{type:String,state:!0}};static styles=[HtmlEditorControl.styles,css`
2
+ :host {
3
+ display: inline-flex;
4
+ }
5
+ `];connectedCallback(){super.connectedCallback(),this.updateEditorMode(),this.editor?.addEventListener("mode-changed",()=>this.updateEditorMode())}handleClick=()=>{this.editor&&this.editor.alignJustify()};updateEditorMode(){this.editor&&(this.editorMode=this.editor.mode)}render(){return this.hidden="code"===this.editorMode,html`
6
+ <button class="${this.buttonClasses}" @click="${this.handleClick}">
7
+ <slot name="icon">
8
+ <k-icon name="format_align_justify"></k-icon>
9
+ </slot>
10
+ <slot></slot>
11
+ </button>
12
+ `}}customElements.define("k-hec-align-justify",AlignJustify);
@@ -0,0 +1,12 @@
1
+ import HtmlEditorControl from"./HtmlEditorControl.js";import{html,css}from"../../lit-all.min.js";import"../Icon.js";export default class AlignLeft extends HtmlEditorControl{static properties={editorMode:{type:String,state:!0}};static styles=[HtmlEditorControl.styles,css`
2
+ :host {
3
+ display: inline-flex;
4
+ }
5
+ `];connectedCallback(){super.connectedCallback(),this.updateEditorMode(),this.editor?.addEventListener("mode-changed",()=>this.updateEditorMode())}handleClick=()=>{this.editor&&this.editor.alignLeft()};updateEditorMode(){this.editor&&(this.editorMode=this.editor.mode)}render(){return this.hidden="code"===this.editorMode,html`
6
+ <button class="${this.buttonClasses}" @click="${this.handleClick}">
7
+ <slot name="icon">
8
+ <k-icon name="format_align_left"></k-icon>
9
+ </slot>
10
+ <slot></slot>
11
+ </button>
12
+ `}}customElements.define("k-hec-align-left",AlignLeft);
@@ -0,0 +1,12 @@
1
+ import HtmlEditorControl from"./HtmlEditorControl.js";import{html,css}from"../../lit-all.min.js";import"../Icon.js";export default class AlignRight extends HtmlEditorControl{static properties={editorMode:{type:String,state:!0}};static styles=[HtmlEditorControl.styles,css`
2
+ :host {
3
+ display: inline-flex;
4
+ }
5
+ `];connectedCallback(){super.connectedCallback(),this.updateEditorMode(),this.editor?.addEventListener("mode-changed",()=>this.updateEditorMode())}handleClick=()=>{this.editor&&this.editor.alignRight()};updateEditorMode(){this.editor&&(this.editorMode=this.editor.mode)}render(){return this.hidden="code"===this.editorMode,html`
6
+ <button class="${this.buttonClasses}" @click="${this.handleClick}">
7
+ <slot name="icon">
8
+ <k-icon name="format_align_right"></k-icon>
9
+ </slot>
10
+ <slot></slot>
11
+ </button>
12
+ `}}customElements.define("k-hec-align-right",AlignRight);