I had a ToggleControl on a block that appears in the InspectorControls area. It is by default on. When I try to set it using the type of boolean it would not save the state properly. When trying to toggle it, it would remain as "on" but the help text would change as if it were off. When saving changes and reloading, the toggle would be back to on.
const defaults: {
autoplay: {
type: 'boolean',
meta: 'autoplay',
default: true
}
}
....
<ToggleControl
label={__('Autoplay')}
help={attributes.autoplay ? __('Slideshow will start playing automatically', 'five') : __('User will have to cycle slideshow manually', 'five')}
checked={(attributes.autoplay || defaults.autoplay.default)}
onChange={() => {
setAttributes({
autoplay: !attributes.autoplay
});
}}
/>
However, if I change that input to be a string and do the boolean handling myself it toggles the input correctly and saves the state:
const defaults: {
autoplay: {
type: 'boolean',
meta: 'autoplay',
default: true
}
}
....
<ToggleControl
label={__('Autoplay')}
help={attributes.autoplay == 'true' ? __('Slideshow will start playing automatically', 'five') : __('User will have to cycle slideshow manually', 'five')}
checked={(attributes.autoplay || defaults.autoplay.default) == 'true'}
onChange={(nextValue) => {
setAttributes({
autoplay: nextValue ? 'true' : 'false'
});
}}
/>
This works, but requires extra characters when checking for truthy when rendering. I could probably make this a little easier by just using a number and using 0 and 1, but that's not the point.
Am I missing something with the boolean type, or is this a bug in Gutenberg?
WP Version: 5.1.1
Your condition inside checked attribute is wrong because it is always evaluating to true. While your help text don't have any of that condition, toggling of help text depends on attributes.autoplay which you are changing inside onChange attribute.
So the solution is to change your condition on checked attribute.
Attributes inside registerBlockType can define default values by using default key inside any attribute so the recommended way is to use that. (default attribute example from wordpress cover block)
Related
I'm starting to use Storybook in an Angular component library.
It works fine for components with inputs like booleans or strings, it shows those inputs using controls.
But there are certain components where the input is an object.
For those components I'm able to provide an object, but users are able to edit a string with the JSON representation of the object instead of several inputs.
How do I do this in a user-friendly way so users can edit those properties in the control without using a JSON representation of the object?
If you're using Knobs, you can write them like this:
This sample here:
class sample{
title: string;
text: string;
settings: {
language: string;
disabled: boolean;
}
}
would turn into this:
template: `
<div style="max-width:80vw;margin:auto;">
<app-custom-component
[title]="this.titleKnob"
[text]="this.textKnob"
[settings]="this.settingsKnob"
></app-custom-component>
</div>
`,
props: {
titleKnob: text('Title',''),
textKnob: text('Text area', ''),
settingsKnob: {
language: text('Default Language', 'en'),
disabled: boolean('Disabled', false),
}
}
I have a base component within which I have a dynamic component with a v-for that displays based on a computed property.
All I've really tried doing thus far, which was an incorrect methodology, was to wrap the method that loads data in a settimeout. This question is as much a methodology question as it is a coding question.
My base component looks like this:
<template>
<div>
<v-progress-linear
v-model="progressValue"
v-if="loading"
></v-progress-linear>
<component
v-for="table in tables"
:key="table.id"
:is="table.structure"
:table="table"
></component>
</div>
</template>
<script>
import Annual from './DataTables/Annual';
import { mapState, mapGetters } from 'vuex';
export default {
name: "Page",
props: [],
components: {
Annual,
},
data: () => ({
progressValue: 0,
loading: false,
tables: [],
}),
computed: {
...mapGetters({
currentTables: 'getCurrentPageTables',
tableTitles: 'getCurrentPageTableTitles',
}),
...mapState({
pageName: state => state.pageName,
snakeName: state => state.snakeName,
}),
methods: {
updateTables(payload) {
this.loading = true;
payload.forEach(title => {
this.tables.push(this.currentTables.filter(e => title === e.name)[0]);
this.progressValue = this.tables.length / payload.length;
})
},
},
watch: {
snakeName: {
handler() {
this.progressValue = 0;
this.updateTables(this.tableTitles);
this.$nextTick(() => {this.loading = false;})
},
immediate: true,
},
}
}
</script>
Annual.vue is simply a component that displays a Vuetify v-data-table element and its structure is fairly inconsequential to this.
For all intents and purposes we can consider currentTables and tableTitles to both be arrays, the first of objects whose data populate the v-data-tables in Annual.vue, and the second of strings which are just the names of the tables.
When the user navigates to another page the getters return different data, based on the page the user navigates to, but some of the pages have over 20 tables, which makes page loading slow upon navigation to these pages. I am trying to do one of two things:
1. Asynchronously load the components one at a time while still making the page functional for the user to navigate through.
2. Display a loader that disappears after all of the content is rendered. I'm having trouble figuring out how to do the latter because I can't put this functionality into the mounted() hook since all of this happens upon the watched parameter changing (hence the component is not re-mounted each time the route changes).
Any advice on how to tackle this would be appreciated.
To learn reason and reason-react, I'm working on a simple “Things 2 Do” app (see source code on GitHub).
I have a TodoItem component that should be rendered with strike-through style when the item has been completed.
I try to solve this by creating a record with various styles, similar to CSS classes, one root style and one for completed items.
type style = {
root: ReactDOMRe.style,
completed: ReactDOMRe.style
};
let styles = {
root: ReactDOMRe.Style.make(), /* add root styles here */
completed: ReactDOMRe.Style.make(~opacity="0.666", ~textDecoration="line-through", ())
};
If the prop completed is true, I combine the root style with the completed style, otherwise I just use the root, like this:
let style = styles.root;
let style = item.completed ? ReactDOMRe.Style.combine(style, styles.completed) : style;
This works, but it seems clunky, so I'm wondering: Is there a more elegant solution, e.g. using a variant and a switch statement?
What is the idiomatic way to create styles for a Reason-React component that depend on props?
Here is the full code of my component:
type item = {
id: int,
title: string,
completed: bool
};
type style = {
root: ReactDOMRe.style,
completed: ReactDOMRe.style
};
let str = ReasonReact.stringToElement;
let component = ReasonReact.statelessComponent("TodoItem");
let styles = {
root: ReactDOMRe.Style.make(), /* add root styles here */
completed: ReactDOMRe.Style.make(~opacity="0.666", ~textDecoration="line-through", ())
};
let make = (~item: item, ~onToggle, _) => {
...component,
render: (_) => {
let style = styles.root;
let style = item.completed ? ReactDOMRe.Style.combine(style, styles.completed) : style;
<div style>
<input
_type="checkbox"
onChange=((_) => onToggle())
checked=(Js.Boolean.to_js_boolean(item.completed))
/>
<label> (str(item.title)) </label>
</div>
}
};
I don't think there's anything that can be called idiomatic yet. The area is quickly changing, and even I have some ideas of my own on how to improve it, but this is more or less how I do it now using bs-css:
module Styles = {
open Css;
let root = completed => style([
color(white),
opacity(completed ? 0.666 : 1.),
textDecoration(completed ? LineThrough : None)
]);
}
...
render: _self =>
<div className=Styles.root(item.completed)>
...
</div>
For now, the way I'm styling my component is OK. There is not really an idiomatic way for styling React components in Reason yet.
The Reason documentation has this to say:
Since CSS-in-JS is all the rage right now, we'll recommend our official pick soon. In the meantime, for inline styles, there's the ReactDOMRe.Style.make API
I would like to change the standard "pen" icon of the
StandardListItem of type DetailAndActive
. Is there a way to do so?
my XML so far:
<List
id="master1List"
items="{/path}"
mode="{device>/listMode}"
select="onSelect"
itemPress="showDetail"
growing="true"
growingScrollToLoad="true">
<items>
<StandardListItem
type="DetailAndActive"
activeIcon="sap-icon://message-information"
id="master1ListItem"
press="onSelect"
title="{title}">
</StandardListItem>
</items>
</List>
As far as I know there are only properties "icon" (which I do not need) and "activeIcon" (which I set but which is also not shown on itemPress/tab). I thought I might change it via css, but it is set in a data-attribute (Icon font, not a uri I could overwrite) and then applied:
.sapUiIcon:before {
content: attr(data-sap-ui-icon-content);
...
Thanks..
[EDIT:]
I accepted the below answer as correct because it works. BUT as you can read in my comment, I'd like to make it possible to accept Controls by using the aggregations metadata like described here:
metadata: {
aggregations: {
"Button" : {type : "sap.m.Button", multiple : false, visibility: "public"}
},
defaultAggregation: "Button"
},
This works so far that that I am now allowed to add a Button control to the ListItem in my XML view, but it is not rendered :-) Any ideas what I miss here additionally?
The icon is hardcoded deep in the control. I found I can extend the StandardListItem to get the result you want like this.
sap.m.StandardListItem.extend('my.StandardListItem', {
renderer: {},
constructor: function(sId, mProperties) {
sap.m.StandardListItem.prototype.constructor.apply(this, arguments);
var sURI = sap.ui.core.IconPool.getIconURI("action");
this._detailIcon =
new sap.ui.core.Icon({
src:sURI})
.setParent(this, null, true)
.addStyleClass("sapMLIBIconDet");
}
});
There is a working example at http://jsbin.com/tuqufe/1/edit?js,output
The bad news is that in the next release (1.28.?) the way that this is done changes significantly so you will need to redo the extended control.
[EDIT:] Sorry I forgot about this one. I just built a quick sample with the OpenUI5 V1.30 beta library. Now the key code looks like this...
sap.m.StandardListItem.extend('my.StandardListItem', {
metadata: {
properties: {
"detailIcon": "string"
}
},
renderer: {},
setDetailIcon: function(icon) {
this.DetailIconURI = sap.ui.core.IconPool.getIconURI(icon);
}
});
There is a sample at http://jsbin.com/bayeje/1/edit?js,output
I'm wondering if it's possible to add custom formatting to redactor? I created a custom button, and I'm able to change the formatting of text, but only using certain elements:
['p', 'blockquote', 'pre', 'h3', 'h4', 'h5']
However, I'm not able to add any of the following:
['small', 'figcaption']
I followed the Redactor docs to set up the button, and here is my function that is being called:
var selected_html = $('#redactor_content').getSelected();
$('#redactor_content').execCommand('formatblock', '<small>');
I also tried adding elements to my 'formattingTags' array, but it didn't seem to have any affect.
formattingTags: ['p', 'blockquote', 'small', 'pre', 'h3', 'h4']
Thank you in advance.
I think I figured it out.
I added the following to my button function:
var $selected_html = $('#redactor_content').getSelected();
$('#redactor_content').execCommand('inserthtml', '<small>' + $selected_html + '</small>');
However, this is not perfect as it does not replace the parent tag, and you can keep adding elements within elements.
Something like that:
redactorOptionsDefaults = {
buttonsAdd: {},
activeButtonsAdd: {},
buttonsCustom: {}
};
redactorOptionsDefaults.buttonsCustom.small = {
title: 'small Header',
callback: function () {
this.formatBlocks('small');
}
}
redactorOptionsDefaults.activeButtonsAdd.small = 'small';
It formatting block, highlight button if needed while selecting block. But don't remove style while repeat button click