Skip to content

Commit 3bf70c1

Browse files
hamza0867tiny-ben-trankemister85
authored
TINY-11908: Add new readonly prop (#463)
* TINY-11908: Add new `readonly` prop Co-authored-by: tiny-ben-tran <ben.tran@tiny.cloud> Co-authored-by: Karl Kemister-Sheppard <karlkemistersheppard@gmail.com>
1 parent d66bfa9 commit 3bf70c1

File tree

7 files changed

+62
-22
lines changed

7 files changed

+62
-22
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

10+
### Added
11+
- The readonly prop now maps to the readonly editor option. See: [Editor important options: readonly](https://www.tiny.cloud/docs/tinymce/latest/editor-important-options/#readonly)
12+
13+
### Changed
14+
- The disabled prop now maps to the disabled editor option. See: [Editor important options: disabled](https://www.tiny.cloud/docs/tinymce/latest/editor-important-options/#disabled)
15+
1016
## 6.1.0 - 2024-10-22
1117

1218
### Changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ This package is a thin wrapper around [TinyMCE](https://github.com/tinymce/tinym
1212

1313
### Support
1414

15+
Version 7.0 is intended to support the tinymce version 7.6 and above.
1516
Version 4.0 is intended to support Vue 3. For Vue 2.x and below please use previous versions of the wrapper.
1617

1718
### Issues

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tinymce/tinymce-vue",
3-
"version": "6.1.1-rc",
3+
"version": "7.0.0-rc",
44
"description": "Official TinyMCE Vue 3 Component",
55
"private": false,
66
"repository": {

src/main/ts/Utils.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ const mergePlugins = (initPlugins: string | string[] | undefined, inputPlugins?:
155155
const isNullOrUndefined = (value: any): value is null | undefined =>
156156
value === null || value === undefined;
157157

158+
const isDisabledOptionSupported = (editor: TinyMCEEditor): boolean => typeof editor.options.set === 'function' && editor.options.isRegistered('disabled');
159+
158160
export {
159161
bindHandlers,
160162
bindModelHandlers,
@@ -163,5 +165,6 @@ export {
163165
uuid,
164166
isTextarea,
165167
mergePlugins,
166-
isNullOrUndefined
168+
isNullOrUndefined,
169+
isDisabledOptionSupported
167170
};

src/main/ts/components/Editor.ts

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import { ScriptLoader } from '../ScriptLoader';
1010
import { getTinymce } from '../TinyMCE';
11-
import { isTextarea, mergePlugins, uuid, isNullOrUndefined, initEditor } from '../Utils';
11+
import { isTextarea, mergePlugins, uuid, isNullOrUndefined, initEditor, isDisabledOptionSupported } from '../Utils';
1212
import { editorProps, IPropTypes } from './EditorPropTypes';
1313
import { h, defineComponent, onMounted, ref, Ref, toRefs, nextTick, watch, onBeforeUnmount, onActivated, onDeactivated } from 'vue';
1414
import type { Editor as TinyMCEEditor, EditorEvent, TinyMCE } from 'tinymce';
@@ -34,9 +34,9 @@ export const Editor = defineComponent({
3434
props: editorProps,
3535
setup: (props: IPropTypes, ctx) => {
3636
let conf = props.init ? { ...props.init, ...defaultInitValues } : { ...defaultInitValues };
37-
const { disabled, modelValue, tagName } = toRefs(props);
37+
const { disabled, readonly, modelValue, tagName } = toRefs(props);
3838
const element: Ref<Element | null> = ref(null);
39-
let vueEditor: any = null;
39+
let vueEditor: TinyMCEEditor | null = null;
4040
const elementId: string = props.id || uuid('tiny-vue');
4141
const inlineEditor: boolean = (props.init && props.init.inline) || props.inline;
4242
const modelBind = !!ctx.attrs['onUpdate:modelValue'];
@@ -52,7 +52,8 @@ export const Editor = defineComponent({
5252
const content = getContent(mounting);
5353
const finalInit = {
5454
...conf,
55-
readonly: props.disabled,
55+
readonly: props.readonly,
56+
disabled: props.disabled,
5657
target: element.value,
5758
plugins: mergePlugins(conf.plugins, props.plugins),
5859
toolbar: props.toolbar || (conf.toolbar),
@@ -72,21 +73,36 @@ export const Editor = defineComponent({
7273
getTinymce().init(finalInit);
7374
mounting = false;
7475
};
76+
watch(readonly, (isReadonly) => {
77+
if (vueEditor !== null && isDisabledOptionSupported(vueEditor)) {
78+
if (typeof vueEditor.mode?.set === 'function') {
79+
vueEditor.mode.set(isReadonly ? 'readonly' : 'design');
80+
} else {
81+
(vueEditor as any).setMode(isReadonly ? 'readonly' : 'design');
82+
}
83+
}
84+
});
7585
watch(disabled, (disable) => {
7686
if (vueEditor !== null) {
77-
if (typeof vueEditor.mode?.set === 'function') {
78-
vueEditor.mode.set(disable ? 'readonly' : 'design');
87+
if (isDisabledOptionSupported(vueEditor)) {
88+
vueEditor.options.set('disabled', disable);
7989
} else {
80-
vueEditor.setMode(disable ? 'readonly' : 'design');
90+
if (typeof vueEditor.mode?.set === 'function') {
91+
vueEditor.mode.set(disable ? 'readonly' : 'design');
92+
} else {
93+
(vueEditor as any).setMode(disable ? 'readonly' : 'design');
94+
}
8195
}
8296
}
8397
});
8498
watch(tagName, (_) => {
85-
if (!modelBind) {
86-
cache = vueEditor.getContent();
99+
if (vueEditor) {
100+
if (!modelBind) {
101+
cache = vueEditor.getContent();
102+
}
103+
getTinymce()?.remove(vueEditor);
104+
nextTick(() => initWrapper());
87105
}
88-
getTinymce()?.remove(vueEditor);
89-
nextTick(() => initWrapper());
90106
});
91107
onMounted(() => {
92108
if (getTinymce() !== null) {
@@ -116,17 +132,21 @@ export const Editor = defineComponent({
116132
}
117133
});
118134
onDeactivated(() => {
119-
if (!modelBind) {
120-
cache = vueEditor.getContent();
135+
if (vueEditor) {
136+
if (!modelBind) {
137+
cache = vueEditor.getContent();
138+
}
139+
getTinymce()?.remove(vueEditor);
121140
}
122-
getTinymce()?.remove(vueEditor);
123141
});
124142
}
125143
const rerender = (init: EditorOptions) => {
126-
cache = vueEditor.getContent();
127-
getTinymce()?.remove(vueEditor);
128-
conf = { ...conf, ...init, ...defaultInitValues };
129-
nextTick(() => initWrapper());
144+
if (vueEditor) {
145+
cache = vueEditor.getContent();
146+
getTinymce()?.remove(vueEditor);
147+
conf = { ...conf, ...init, ...defaultInitValues };
148+
nextTick(() => initWrapper());
149+
}
130150
};
131151
ctx.expose({
132152
rerender,

src/main/ts/components/EditorPropTypes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface IPropTypes {
2626
toolbar: string[] | string;
2727
modelValue: string;
2828
disabled: boolean;
29+
readonly: boolean;
2930
tinymceScriptSrc: string;
3031
}
3132

@@ -43,6 +44,7 @@ export const editorProps: CopyProps<IPropTypes> = {
4344
toolbar: [ String, Array ],
4445
modelValue: String,
4546
disabled: Boolean,
47+
readonly: Boolean,
4648
tinymceScriptSrc: String,
4749
outputFormat: {
4850
type: String,

src/stories/Editor.stories.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { Story } from '@storybook/vue3';
22
import { onBeforeMount, ref } from 'vue';
33
import { ScriptLoader } from '../main/ts/ScriptLoader';
44

5+
import type { EditorEvent, Editor as TinyMCEEditor } from 'tinymce';
56
import { Editor } from '../main/ts/components/Editor';
6-
import type { Editor as TinyMCEEditor, EditorEvent } from 'tinymce';
77

88
const apiKey = 'qagffr3pkuv17a8on1afax661irst1hbr4e6tbv888sz91jc';
99
const content = `
@@ -159,24 +159,32 @@ export const Disable: Story = (args) => ({
159159
const cc = args.channel || lastChannel;
160160
const conf = getConf(args.conf);
161161
const disabled = ref(false);
162+
const readonly = ref(false);
162163
const toggleDisabled = (_) => {
163164
disabled.value = !disabled.value;
164165
}
166+
const toggleReadonly = (_) => {
167+
readonly.value = !readonly.value;
168+
}
165169
return {
166170
apiKey,
167171
content,
168172
cloudChannel: cc,
169173
conf,
170174
disabled,
171-
toggleDisabled
175+
readonly,
176+
toggleDisabled,
177+
toggleReadonly
172178
}
173179
},
174180
template: `
175181
<div>
176182
<button @click="toggleDisabled">{{ disabled ? 'enable' : 'disable' }}</button>
183+
<button @click="toggleReadonly">{{ readonly ? 'design' : 'readonly' }}</button>
177184
<editor
178185
api-key="${apiKey}"
179186
v-bind:disabled="disabled"
187+
v-bind:readonly="readonly"
180188
:init="conf"
181189
v-model="content"
182190
/>

0 commit comments

Comments
 (0)