ketekny-ui-kit 1.0.43 → 1.0.44

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/ui/kEditor.vue +68 -19
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ketekny-ui-kit",
3
3
  "type": "module",
4
- "version": "1.0.43",
4
+ "version": "1.0.44",
5
5
  "description": "A Vue 3 UI component library with Tailwind CSS styling",
6
6
  "main": "index.js",
7
7
  "files": [
@@ -21,10 +21,11 @@
21
21
  <div
22
22
  class="editor-content"
23
23
  :class="[defaultStyle, hasError ? errorStyle : '', disabled ? disabledStyle : '']"
24
- contenteditable
24
+ :contenteditable="!disabled"
25
25
  ref="editor"
26
- @input="updateValue"
27
- @blur="updateValue"
26
+ @focus="handleFocus"
27
+ @input="handleInput"
28
+ @blur="handleBlur"
28
29
  :placeholder="placeholder"
29
30
  ></div>
30
31
  <div class="text-sm text-red-500 dark:text-rose-400" v-if="hasError && error !== true && error !== ''">
@@ -61,13 +62,15 @@ export default {
61
62
  default: "Πληκτρολογήστε κείμενο...",
62
63
  },
63
64
  },
64
- emits: ["update:modelValue"],
65
+ emits: ["update:modelValue", "input", "focus", "blur", "change"],
65
66
  data() {
66
67
  return {
67
68
  defaultStyle:
68
69
  "w-full px-3 py-2 border rounded-b-lg transition shadow-sm focus:outline-none focus:ring-2 focus:ring-primary/20 min-h-[150px] focus:border-primary bg-white placeholder-gray-400 !list-disc !list-inside prose dark:bg-slate-800 dark:text-slate-100 dark:border-slate-600 dark:placeholder-slate-500",
69
70
  errorStyle: "border-red-500 focus:ring focus:ring-red-300 dark:border-rose-500 dark:focus:ring-rose-500/20",
70
71
  disabledStyle: "!bg-gray-100 !text-gray-400 !cursor-not-allowed editor pointer-events-none select-none dark:!bg-slate-900 dark:!text-slate-500",
72
+ isFocused: false,
73
+ pendingModelValue: null,
71
74
  toolbar: [
72
75
  { command: "bold", icon: Bold, title: "Bold" },
73
76
  { command: "italic", icon: Italic, title: "Italic" },
@@ -77,34 +80,80 @@ export default {
77
80
  };
78
81
  },
79
82
  mounted() {
80
- this.$refs.editor.innerHTML = this.modelValue || "";
83
+ this.setEditorHtml(this.modelValue || "");
81
84
  },
82
85
  methods: {
83
- exec(command, value = null) {
84
- document.execCommand(command, false, value);
85
- this.updateValue();
86
+ getEditorHtml() {
87
+ return this.$refs.editor?.innerHTML || "";
88
+ },
89
+ setEditorHtml(value) {
90
+ if (!this.$refs.editor) return;
91
+ this.$refs.editor.innerHTML = value || "";
86
92
  },
87
- updateValue() {
93
+ normalizeEditorDom() {
88
94
  const el = this.$refs.editor;
95
+ if (!el) return "";
89
96
 
90
- // Optional cleanup for <div> -> <p> if outside of lists
91
- el.querySelectorAll("div").forEach((div) => {
92
- if (!div.closest("ul") && div.innerText.trim().length && div.tagName.toLowerCase() !== "li") {
93
- const p = document.createElement("p");
94
- p.innerHTML = div.innerHTML;
95
- div.replaceWith(p);
96
- }
97
+ Array.from(el.children).forEach((child) => {
98
+ if (child.tagName !== "DIV" || !child.innerText.trim().length) return;
99
+
100
+ const p = document.createElement("p");
101
+ p.innerHTML = child.innerHTML;
102
+ child.replaceWith(p);
97
103
  });
98
104
 
99
- const html = el.innerHTML;
105
+ return this.getEditorHtml();
106
+ },
107
+ exec(command, value = null) {
108
+ this.$refs.editor?.focus();
109
+ document.execCommand(command, false, value);
110
+ this.handleInput();
111
+ },
112
+ handleFocus(event) {
113
+ this.isFocused = true;
114
+ this.$emit("focus", event);
115
+ },
116
+ handleInput() {
117
+ const html = this.getEditorHtml();
118
+ this.pendingModelValue = null;
119
+ this.$emit("input", html);
100
120
  this.$emit("update:modelValue", html);
101
121
  },
122
+ handleBlur(event) {
123
+ this.isFocused = false;
124
+ this.$emit("blur", event);
125
+
126
+ if (this.pendingModelValue != null) {
127
+ const nextValue = this.pendingModelValue;
128
+ this.pendingModelValue = null;
129
+ this.setEditorHtml(nextValue);
130
+ this.$emit("update:modelValue", nextValue);
131
+ this.$emit("change", nextValue);
132
+ return;
133
+ }
134
+
135
+ const normalized = this.normalizeEditorDom();
136
+ this.$emit("change", normalized);
137
+ this.$emit("update:modelValue", normalized);
138
+ },
102
139
  },
103
140
  watch: {
104
141
  modelValue(newVal) {
105
- if (this.$refs.editor && this.$refs.editor.innerHTML !== newVal) {
106
- this.$refs.editor.innerHTML = newVal || "";
142
+ if (!this.$refs.editor) return;
143
+
144
+ const nextValue = newVal || "";
145
+ if (this.getEditorHtml() === nextValue) {
146
+ this.pendingModelValue = null;
147
+ return;
148
+ }
149
+
150
+ if (this.isFocused) {
151
+ this.pendingModelValue = nextValue;
152
+ return;
107
153
  }
154
+
155
+ this.pendingModelValue = null;
156
+ this.setEditorHtml(nextValue);
108
157
  },
109
158
  },
110
159
  };