The CKEditor 5 documentation shows the easy way how to add special characters to CKEditor 5 (https://ckeditor.com/docs/ckeditor5/latest/features/special-characters.html):
import SpecialCharacters from '#ckeditor/ckeditor5-special-characters/src/specialcharacters';
import SpecialCharactersEssentials from '#ckeditor/ckeditor5-special-characters/src/specialcharactersessentials';
function SpecialCharactersEmoji( editor ) {
editor.plugins.get( 'SpecialCharacters' ).addItems( 'Emoji', [
{ title: 'smiley face', character: '😊' },
{ title: 'rocket', character: '🚀' },
{ title: 'wind blowing face', character: '🌬️' },
{ title: 'floppy disk', character: '💾' },
{ title: 'heart', character: '❤️' }
] );
}
ClassicEditor
.create( document.querySelector( '#editor' ), {
plugins: [
SpecialCharacters, SpecialCharactersEssentials, SpecialCharactersEmoji,
// Other plugins...
],
toolbar: [ 'specialCharacters', ... ],
} )
.then( ... )
.catch( ... );
But I have not found how to change it in Drupal 9. Does any special hook exist for it?
I have tried to create a new module based on starter template (https://git.drupalcode.org/project/ckeditor5_dev/-/tree/1.0.x/ckeditor5_plugin_starter_template):
chars.info.yml
name: Characters
type: module
description: "Provides CKEditor plugin."
package: CKEditor 5
core_version_requirement: ^9.2 | ^10.0
dependencies:
- drupal:ckeditor5
chars.ckeditor5.yml
chars_chars:
ckeditor5:
plugins:
- chars.Chars
drupal:
label: Characters
library: chars/chars
elements: false
chars.libraries.yml
chars:
js:
js/build/chars.js: { preprocess: false, minified: true }
dependencies:
- core/ckeditor5
ckeditor5_plugins/chars/src/chars.js
import { Plugin } from 'ckeditor5/src/core';
import SpecialCharacters from '#ckeditor/ckeditor5-special-characters/src/specialcharacters';
export default class Chars extends Plugin {
init() {
const editor = this.editor;
editor.plugins.get( 'SpecialCharacters' ).addItems( 'Emoji', [
{ title: 'smiley face', character: '😊' },
{ title: 'rocket', character: '🚀' },
{ title: 'wind blowing face', character: '🌬️' },
{ title: 'floppy disk', character: '💾' },
{ title: 'heart', character: '❤️' }
] );
}
static get pluginName() {
return 'Chars';
}
static get requires() {
return [SpecialCharacters];
}
}
This code was built by yarn run build and insert to js/build/folder. When CKEditor is loaded the console throws: CKEditorError: plugincollection-plugin-not-found {"plugin":null}
Related
Trying to add column headings to a bootstrap table in a Vue 3 project using i18n dependency:
"vue-i18n": "^9.3.0-beta.10",
computed: {
fields() {
return [
{ key: "formID", label: this.transColHeading("status.form_id"), sortable: true }
];
}
},
methods: {
transColHeading(colHeading) {
return this.$i18n.t(colHeading);
},
}
But I am getting the error
TypeError: this.$i18n.t is not a function
Any idea how to make this work?
For info if I console log this.$i18n.locale I get the correct language.
When I go and try to run gatsby develop I get the following error:
TypeError: Cannot destructure property 'fields' of 'nodesType' as it is undefined.
at generateNodeQueriesFromIngestibleFields (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/steps/ingest-remote-schema/buil
d-queries-from-introspection/generate-queries-from-ingestable-types.js:155:13)
at buildNodeQueries (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/steps/ingest-remote-schema/build-queries-from-introspe
ction/build-node-queries.js:25:25)
at runSteps (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/utils/run-steps.ts:41:9)
at runSteps (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/utils/run-steps.ts:43:9)
at ingestRemoteSchema (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/steps/ingest-remote-schema/index.js:49:5)
at runSteps (/Users/repoFolder/Work/gatsby/node_modules/gatsby-source-wordpress/src/utils/run-steps.ts:41:9)
at runAPI (/Users/repoFolder/Work/gatsby/node_modules/gatsby/src/utils/api-runner-node.js:487:16)
ERROR #gatsby-source-wordpress_112003
gatsby-source-wordpress
Encountered a critical error when running the buildNodeQueries build step.
See above for more information.
not finished createSchemaCustomization - 3.831s
not finished [gatsby-source-wordpress] ingest WPGraphQL schema - 2.379s
I haven't made any changes, or upgraded packages and have since been working on other projects. I tried disabling the Related Posts plugin, as well as a few others in addition to upgrading the gatsby-source-wordpress plugin.
I am configuring the plugin like in my gatsby-config.js:
{
resolve: "gatsby-source-wordpress",
options: {
url: `${process.env.WPGRAPHQL_URL}`,
verbose: false,
develop: {
hardCacheData: false,
},
schema: {
perPage: 10, // currently set to 100
requestConcurrency: 3, // currently set to 15
previewRequestConcurrency: 1, // currently set to 5
},
type: {
Page: {
exclude: true,
},
Menu: {
exclude: true,
},
MenuItem: {
exclude: true,
},
},
debug: {
graphql: {
onlyReportCriticalErrors: true,
},
},
searchAndReplace: [
{
search: `${process.env.GATSBY_WORDPRESS_URL_PROTOCOL}://${process.env.GATSBY_WORDPRESS_URL_PATH}`,
replace: `${process.env.GATSBY_SITE_URL_PROTOCOL}://${process.env.GATSBY_SITE_URL_PATH}`,
},
],
html: {
useGatsbyImage: true,
},
},
},
and I am configuring the plugin / constructing the node ingatsby-node.jslike the following:
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const BlogPostTemplate = path.resolve("./src/templates/BlogPost.tsx")
const BlogTagPostsTemplate = path.resolve(
"./src/templates/BlogTagPosts.tsx"
)
const BlogCategoryPostsTemplate = path.resolve(
"./src/templates/BlogCategoryPosts.tsx"
)
const BlogPostsResult = await graphql(`
{
allWpPost {
edges {
node {
id
slug
uri
link
title
excerpt
date(formatString: "MMMM DD, YYYY")
modified(formatString: "MMMM DD, YYYY")
author {
node {
avatar {
url
}
id
name
uri
slug
}
}
featuredImage {
node {
localFile {
childImageSharp {
gatsbyImageData(
width: 1920
placeholder: BLURRED
formats: [AUTO, WEBP, AVIF]
)
}
}
}
}
categories {
nodes {
id
count
name
slug
}
}
tags {
nodes {
id
count
name
slug
}
}
}
}
}
}
`)
if (BlogPostsResult.errors) {
reporter.panicOnBuild("Error while running GraphQL query.")
return
}
const BlogPosts = BlogPostsResult.data.allWpPost.edges
BlogPosts.forEach((post, index) => {
const date = post.node.date
createPage({
path: `/${post.node.categories.nodes[0].slug}/${moment(date).format(
"YYYY"
)}/${moment(date).format("MM")}/${post.node.slug}.html`,
component: BlogPostTemplate,
context: {
id: post.node.id,
slug: post.node.slug,
uri: post.node.uri,
previous: index === 0 ? null : BlogPosts[index - 1].node,
next:
index === BlogPosts.length - 1
? null
: BlogPosts[index + 1].node,
},
})
})
createPaginatedPages({
edges: BlogPosts,
createPage: createPage,
pageTemplate: "src/templates/BlogPosts.tsx",
pageLength: 10,
pathPrefix: "blog",
})
const BlogTagPosts = new Map()
const BlogCategoryPosts = new Map()
BlogPosts.forEach((post) => {
const tags = post.node.tags.nodes
if (tags && tags.length > 0) {
tags.forEach((tag) => {
if (BlogTagPosts.has(tag.slug)) {
BlogTagPosts.set(tag.slug, [
...BlogTagPosts.get(tag.slug),
post,
])
} else {
BlogTagPosts.set(tag.slug, [post])
}
if (BlogTagPosts.has(tag.title)) {
BlogTagPosts.set(tag.title, [
...BlogTagPosts.get(tag.title),
post,
])
} else {
BlogTagPosts.set(tag.title, [post])
}
})
}
const categories = post.node.categories.nodes
if (categories && categories.length > 0) {
categories.forEach((category) => {
if (BlogCategoryPosts.has(category.slug)) {
BlogCategoryPosts.set(category.slug, [
...BlogCategoryPosts.get(category.slug),
post,
])
} else {
BlogCategoryPosts.set(category.slug, [post])
}
if (BlogCategoryPosts.has(category.title)) {
BlogCategoryPosts.set(category.title, [
...BlogCategoryPosts.get(category.title),
post,
])
} else {
BlogCategoryPosts.set(category.title, [post])
}
})
}
})
const BlogTagSlugs = [...BlogTagPosts.keys()]
const BlogCategorySlugs = [...BlogCategoryPosts.keys()]
//const BlogCategoryTitles = [...BlogCategoryPosts.keys()];
if (BlogTagSlugs.length > 0) {
BlogTagSlugs.forEach((BlogTagSlug) => {
createPage({
path: `/tag/${BlogTagSlug}`,
component: BlogTagPostsTemplate,
context: {
group: BlogTagPosts.get(BlogTagSlug),
slug: BlogTagSlug,
title: BlogTagPosts.get("tag.title"),
},
})
})
}
if (BlogCategorySlugs.length > 0) {
BlogCategorySlugs.forEach((BlogCategorySlug) => {
createPage({
path: `/category/${BlogCategorySlug}`,
component: BlogCategoryPostsTemplate,
context: {
group: BlogCategoryPosts.get(BlogCategorySlug),
slug: BlogCategorySlug,
title: BlogCategoryPosts.get("category.title"),
},
})
})
}
}
Where do I start?
I have tried upgrading packages, stripping out the config option in the config file, and deleting node_modules, package-lock.json and reinstalling the packages. I do run into errors when trying a normal npm i command, but that is fixed by adding the flag -legacy-peer-deps.
I also got this same error, but I got this after upgrading my WordPress plugins. Im pretty sure this is due to the new version 13.x of WP GraphQL. You can fix this by downgrading the WP GraphQL WordPress plugin to version 12.3. You can grab that v12 version here. Also more info on this issue and if a fix is implemented it will probably be updated here.
Had exactly the same issue.
Downgrading WP GraphQL to version 1.12.3 fixed this for me.
Here's the link wp-graphql.1.12.3
My eslint .eslintrc.js, now properly in the src folder, is the following:
module.exports = {
env: {
browser: true,
commonjs: true,
es2021: true
},
extends: [
'plugin:vue/vue3-recommended',
'standard',
'prettier'
],
parserOptions: {
ecmaVersion: 2020,
parser: '#typescript-eslint/parser',
'ecmaFeatures': {
'jsx': true
}
},
plugins: [
'vue',
'#typescript-eslint'
],
rules: {
'import/no-unresolved': 'error'
},
settings: {
'import/parsers': {
'#typescript-eslint/parser': ['.ts', '.tsx']
},
'import/resolver': {
'typescript': {
'alwaysTryTypes': true,
}
}
}
}
I'm attempting to use eslint-import-resolver-typescript, but the documentation is a bit opaque.
I currently get errors on lines where a externally defined type is used (StepData in this example):
setup() {
const data = inject("StepData") as StepData;
return {
data,
};
},
The answer was the following. In types.d.ts (or other file if you want to have different collections of your custom types):
export interface MyType {
positioner: DOMRect;
content: DOMRect;
arrow: DOMRect;
window: DOMRect;
}
export interface SomeOtherType {
.. and so on
Then in the .vue files, import the types I need for the component:
import type { MyType, SomeOtherType } from "../types/types";
Before I was not using the export keyword and the types just worked without being imported. They have to be imported like this if you use export. It's kind of amazing how you are just expected to know this, the documentation for Typescript or Vue is sorely lacking in examples.
I am using Nuxt 2.14.3 and I want to use the fullcalendar library. For this I have added the following packages via Yarn:
yarn add #fullcalendar/vue
yarn add #fullcalendar/interaction
yarn add #fullcalendar/daygrid
yarn add #fullcalendar/timegrid
yarn add #fullcalendar/list
My Vue template file looks like this:
<template>
<client-only placeholder="loading...">
<FullCalendar class="demo-app-calendar" :options="calendarOptions">
<template #eventContent="arg">
<b>{{ arg.timeText }}</b>
<i>{{ arg.event.title }}</i>
</template>
</FullCalendar>
</client-only>
</template>
<script>
import FullCalendar from '#fullcalendar/vue'
import dayGridPlugin from '#fullcalendar/daygrid'
import timeGridPlugin from '#fullcalendar/timegrid'
import interactionPlugin from '#fullcalendar/interaction'
export default {
components: {
FullCalendar,
},
mixins: [
page({
meta() {
return {
title: this.$tU('meta_title_vrm_planning'),
}
},
}),
],
data() {
return {
calendarOptions: {
plugins: [
dayGridPlugin,
timeGridPlugin,
interactionPlugin, // needed for dateClick
],
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay',
},
initialView: 'dayGridMonth',
initialEvents: [
{
id: 1,
title: 'All-day event',
start: new Date().toISOString().replace(/T.*$/, ''),
},
{
id: 2,
title: 'Timed event',
start: `${new Date().toISOString().replace(/T.*$/, '')}T12:00:00`,
},
], // alternatively, use the `events` setting to fetch from a feed
editable: true,
selectable: true,
selectMirror: true,
dayMaxEvents: true,
weekends: true,
select: this.handleDateSelect,
eventClick: this.handleEventClick,
eventsSet: this.handleEvents,
/* you can update a remote database when these fire:
eventAdd:
eventChange:
eventRemove:
*/
},
currentEvents: [],
}
},
methods: {
handleDateSelect(selectInfo) {
const title = prompt('Please enter a new title for your event')
const calendarApi = selectInfo.view.calendar
calendarApi.unselect() // clear date selection
if (title) {
calendarApi.addEvent({
id: 3,
title,
start: selectInfo.startStr,
end: selectInfo.endStr,
allDay: selectInfo.allDay,
})
}
},
handleEventClick(clickInfo) {
if (confirm(`Are you sure you want to delete the event '${clickInfo.event.title}'`)) {
clickInfo.event.remove()
}
},
handleEvents(events) {
this.currentEvents = events
},
},
}
</script>
It gives me the following errors:
[Vue warn]: Error in mounted hook: "TypeError: DateProfileGeneratorClass is not a constructor"
found in
---> <FullCalendar>
and
TypeError: DateProfileGeneratorClass is not a constructor
at buildDateProfileGenerator (main.js?d610:7232)
and also
TypeError: Cannot read property 'destroy' of undefined
at VueComponent.beforeDestroy (FullCalendar.js?ff68:33)
Note: this works perfectly when accessing my page from another page, but when going directly to the page URL (or when clicking CTRL+F5) I receive the errors as mentioned above.
Note 2: I tried transpiling the library in Nuxt config as suggested in the example repo https://github.com/fullcalendar/fullcalendar-example-projects/tree/master/nuxt, but it gives me the exact same errors, but with the .ts files instead of the .js files.
how can i change the button-text ("Done" and "Cancel") in the selectfield to german or in any text i like?
xtype: 'selectfield',
name: 'sector',
width: 150,
prependText: 'Sector:',
options: [
{text: 'Alle Termine', value: 'alldates'},
]
one easy possibilty would be to override the defaults of the picker, e.g.:
Ext.override(Ext.Picker, {
doneButton: 'Fertig',
cancelButton: 'Abbrechen'
});
You can extend Ext.form.Select to allow you to apply your own configuration to the picker it uses.
Ext.ns('MySite.ux.form');
MySite.ux.form.Select = Ext.extend(Ext.form.Select , {
getPicker: function() {
if (!this.picker) {
this.picker = new Ext.Picker(Ext.apply({
slots: [{
align : 'center',
name : this.name,
valueField : this.valueField,
displayField: this.displayField,
value : this.getValue(),
store : this.store
}],
listeners: {
change: this.onPickerChange,
scope: this
}
}, this.pickerConfig));
}
return this.picker;
}
});
Ext.reg('myselectfield', MySite.ux.form.Select);
And your selectfield configuration might look like this:
{
xtype: 'myselectfield',
name: 'sector',
label: 'Sector',
pickerConfig: {
doneButton: 'Fertig',
cancelButton: 'Abbrechen'
}
}