I'm trying to create a custom module that attaches a class to the html element and uses the value that is inside the static create function to add it to the dataset.
so what I do is:
write some text
click the button
Expected Behaviour
:
The text gets wrapped in a bold tag with class "bold" and data-id=1
Result:
The text gets wrapped in a bold tag with
class "bold"
data-id=1.
Then the Placeholder create function is called once again with value = true, so the value changes to
class "bold"
data-id=true.
Does anybody know why this happens? Why is the Placeholder create function called twice? Also, I suspect this is something related to react, because I tried it on a simple js example and this doesn't happen
import ReactQuill,{Quill} from 'react-quill'
import Bold from "./Bold.ts"
Quill.register(Bold);
const Editor = () => {
const ref = useRef();
const [value, setValue] = useState("");
const modules = {
toolbar: [
[{ 'header': [1, 2, false] }],
['bold', 'italic', 'underline','strike', 'blockquote'],
[{'list': 'ordered'}, {'list': 'bullet'}, {'indent': '-1'}, {'indent': '+1'}],
[{ 'color': [] }, { 'background': [] }],
['clean']
],
}
const onChange = (content:any, delta:Delta) => {
setValue(content)
}
useEffect(()=>{
console.log("value is changing:", value)
},[value])
// #ts-ignore
const format = ()=>ref.current.editor.formatText(0,ref.current.editor.getText().length,"mybold", 1)
return (
<div>
<Button onClick={format} >style me</Button>
<ReactQuill ref={ref as LegacyRef<any>} modules={modules} theme="bubble" value={value} onChange={onChange}/>
</div>
);
}
Bold.ts
import Quill from "quill";
let Inline = Quill.import('blots/inline');
export default class Bold extends Inline {
static create(value:any){
const node = super.create()
console.log("value", value)
node.setAttribute("class","bold")
node.dataset["id"]= value
return node
}
}
Bold.blotName = 'mybold';
Bold.tagName = 'bold';
Related
I am trying to create pivot table using pivottable.js, my Pivot Options are
import PivotTableUI from "react-pivottable/PivotTableUI"
import TableRenderers from "react-pivottable/TableRenderers"
import "react-pivottable/pivottable.css"
import Plot from 'react-plotly.js'
const PlotlyRenderers = createPlotlyRenderers(Plot)
const [pivotTableUIConfig, setPivotTableUIConfig] = useState({})
const pivotPresets = {
rendererName,
aggregatorName,
plotlyOptions: {width: 900, height: 500},
plotlyConfig: {},
rendererOptions: {
table: {
clickCallback(e, value, filters, pivotData) {
const names = []
pivotData.forEachMatchingRecord(filters,
function(record) { names.push(record.TaskID) })
alert(names.join("\n"))
}
}
}
}
My UI for react is
<PivotTableUI data={data} onChange={(s) => {
setPivotTableUIConfig(s)
setdata(s)
}} unusedOrientationCutoff={Infinity} {...pivotPresets}
renderers={Object.assign({}, TableRenderers, PlotlyRenderers)} {...pivotTableUIConfig} />
I want to save my filter configuration in cookie but there is no documentation of how to do it in react js.
It's availiable in Jquery https://pivottable.js.org/examples/save_restore.html
Please help thanks.
I have 'textRef' created in parent component and passed to children component using createRef().
I am trying to dynamically focus Input in children component on props change.
It does work when I test on localhost (chrome) but not on web view.
Any advice on this issue ?
Thanks !!
parent component
const UserAddressForm = ({ query }) => {
const textRef = useRef(null);
const {
state: { newAddress },
saveMultiData,
} = query;
useEffect(() => {
if (textRef && textRef.current) {
textRef.current.focus();
}
}, [newAddress.zipcode]);
const onAddressChange = (type, value) => {
const addressObj = {
...newAddress,
};
...
saveMultiData({ newAddress: addressObj });
};
return (
<InfoUl margin="21px 0px 0px;">
...
<TextField
ref={textRef}
label=""
name="addr2"
placeholder="상세주소 입력"
textField={newAddress}
onInputChange={e => onAddressChange('addr2', e.target.value)}
/>
</InfoUl>
);
};
children component
import React from 'react';
import PropTypes from 'prop-types';
import { LabelBox, InputBox } from './styles';
const TextField = React.forwardRef(
(
{
label = null,
name,
type,
placeholder,
textField,
onInputChange,
autoComplete,
pattern,
disabled,
width,
flex,
marginRight,
marginBottom,
},
ref,
) => (
<LabelBox width={width} marginBottom={marginBottom}>
{label !== null && <label htmlFor={label}>{label}</label>}
<InputBox flex={flex} marginRight={marginRight}>
<input
ref={ref}
type={type || 'text'}
name={name}
placeholder={placeholder}
value={textField[name] || ''}
onChange={onInputChange}
autoComplete={autoComplete || 'on'}
pattern={pattern || null}
disabled={!!disabled}
/>
</InputBox>
</LabelBox>
),
);
version
React v16.9.0
I resolved it by using input autoFocus attribute as well as ref attribute.
Since input appears dynamically on a button click, ref.focus won't work.
AutoFocus will get focus when the input appears.
Then Ref will get re-focus where the input is already placed on address re-search.
I am creating a plugin for CKEditor 5, and I can't figure out how to set the title attribute on an <img> element using writer.setAttribute('title', ...).
I have tried stuff like schema.extend but to no avail.
The thing is, code works flawlessly when operating on the alt attribute.
Am I missing something?
My plugin code:
const ButtonView = require('#ckeditor/ckeditor5-ui/src/button/buttonview').default;
const imageIcon = require('#ckeditor/ckeditor5-core/theme/icons/low-vision.svg').default;
export default class ImageTextTitle extends Plugin {
init() {
const editor = this.editor;
editor.ui.componentFactory.add('imageTextTitle', locale => {
const view = new ButtonView(locale);
view.set({
label: 'Insert image title',
icon: imageIcon,
tooltip: true
});
view.on('execute', () => {
const newTitle = prompt('New image title');
const selection = editor.model.document.selection;
const imageElement = selection.getSelectedElement();
if (newTitle !== null) {
editor.model.change(writer => {
writer.setAttribute('title', newTitle, imageElement);
});
}
});
return view;
});
}
}
I am using React-typescript for my app. for state management I am using Redux-toolkit. I am fetching one open api and store it my redux store. I created dispatch function. From the component when I click the dispatch function then it will display random dog image. But the problem is after mapping the when I am using this img src. I am getting typescript error: Type 'Data' is not assignable to type 'string'. I don't know what I am doing wrong. i uploaded my code in codesandbox, although it works in codesandbox but does not work in my app.
Ps. I did not upload my store setup code because it works find ☺️.
This is my reducer
/* eslint-disable #typescript-eslint/indent */
import { createSlice, PayloadAction } from '#reduxjs/toolkit';
import { AppThunk } from "store/store";
interface IMeta {
loading: boolean;
error: boolean;
message: string;
}
interface Data {
src: string;
}
interface IDogs {
meta: IMeta;
dogs: Data[];
}
const initialState: IDogs = {
"meta": {
"loading": false,
"error": false,
"message": ``
},
"dogs": []
};
const dogSlice = createSlice({
"name": `random-dogs`,
initialState,
"reducers": {
loadState(state) {
state.meta = {
"loading": true,
"error": false,
"message": ``
};
state.dogs = [];
},
fetchData(state, action: PayloadAction<Data[]>) {
state.meta.loading = false;
state.dogs = action.payload;
console.log(`dogs`, action.payload);
},
loadFailed(state, action: PayloadAction<string>) {
state.meta = {
"loading": false,
"error": true,
"message": action.payload
};
state.dogs = [];
}
}
});
export const { loadState, fetchData, loadFailed } = dogSlice.actions;
export default dogSlice.reducer;
export const fetchDogs = (): AppThunk => async (dispatch) => {
const url = `https://dog.ceo/api/breeds/image/random/5`;
try {
dispatch(loadState);
const response = await fetch(url);
const data = await response.json();
console.log(data);
console.log(data.message);
const singleData = data.message.map((i) => i);
dispatch(fetchData(singleData));
} catch (error) {
dispatch(loadFailed(`dogs are unavailable`));
console.log({ error });
}
};
This is the component I am using the redux store
import React, { memo } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchDogs } from 'store/dogs';
import { RootState } from 'store/combineReducer';
export default memo(() => {
const state = useSelector((rootState: RootState) => ({
"dogs": rootState.fetchDogs.dogs,
"meta": rootState.fetchDogs.meta
}));
const dispatch = useDispatch();
console.log(`Dog component`, state.dogs[0]);
return (
<div>
{state.meta.loading ? <p>loading....</p> :
state.dogs.map((i, index) =>
<div key={index}>
<ul>
<li>{i}</li> // I can see the strings
</ul>
<img style={{ "width": 50, "height": 50 }} src={i} /> //getting error in here
</div>)}
<br></br>
<button onClick={() => dispatch(fetchDogs())}> display random dogs</button>
</div>
);
});
The situation is as follows:
Interface IDog is has a property "dogs" of type Data[].
Data has a property "src" of type String.
Src attribute of an img needs to be a string.
You are now passing IDogs.dogs. You need to go deeper to IDogs.dogs.src to get the source string you want.
So line 25 of App.tsx should look like this and all seems to work fine:
<img style={{ width: 50, height: 50 }} src={i.src} alt="dog" />
PS: The codesandbox example still works as it apparently does some kind of assumption that you want the src property, but as you see you still get the error.
EDIT: After some fiddling the answer is as below. It is however connected to what was written above.
I downloaded you project and tried to run in npm on my PC. I did 2 things to make it work:
I updated line 25 to use the cast: src={String(i)}
I updated react-scripts. See this thread for reference: TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string. Received type undefined raised when starting react app
I made a inline widget similar a placeholder (ckeditor4), but now I want to render a dropdown when the widget is selected to show options values to replace the placeholder. I trying use BalloonPanelView but no success until now, someone have a idea about how to make it?
this.editor.editing.view.document.on('click', (evt, data) => {
evt.stop();
const element = data.target;
if (element && element.hasClass('placeholder')) {
if (!element.getAttribute('data-is-fixed')) {
const balloonPanelView = new BalloonPanelView();
balloonPanelView.render();
['option1', 'option2', 'option3'].forEach((value) => {
const view = new View();
view.set({
label: value,
withText: true
});
balloonPanelView.content.add(view);
});
balloonPanelView.pin({
target: element
});
}
}
});
I found the solution using ContextualBalloon class:
import ContextualBalloon from "#ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon";
// Define ballon
const balloon = editor.plugins.get(ContextualBalloon);
const placeholderOptions = // Here I defined list with buttons '<li><button></li>'
// Finnaly render ballon
balloon.add({
view: placeholderOptions,
singleViewMode: true,
position: {
target: data.domTarget
}
});