How to set muliple class variables in riot component - javascript

I've made a header component with riot.js that should have different styling on some pages. I'd like to set the classes when mounting the tag in the html document, but can't figure out how. The tag looks like this:
<o-header>
<header class={ opts.structure, opts.overflow, opts.color }>
...
</header>
</o-header>
And here is the mounting:
riot.mount('o-header',
{
structure: 'o-header',
overflow: 's-overflow--hidden',
color: 'm-header--navy',
})
Is it a syntax error or am I misusing the opts feature here?

This is an interesting use case for options.
The format for providing multiple dynamic classes is the following,
<o-header>
<header class="{opts.structure} {opts.overflow} {opts.color}">
...
</header>
</o-header>
To take it a step further, you could apply this syntax to conditional classes,
<o-header>
<header class="{true ? opts.structure: ''} {true ? opts.color : ''}">
...
</header>
</o-header>
References:
JSFiddle: https://jsfiddle.net/31bokmyx/
RiotJS Issue: https://github.com/riot/riot/issues/2073
Conditional Dynamic Classes JSFiddle: https://jsfiddle.net/31bokmyx/19/

Related

Vue - Surround a component with a tag dynamically

What I'm trying to say is the v-if directive can make an entire component (and all its content) disappear based on a condition.
My question is:
is there a way to make only the surrounding tag or component disappear, without removing its content?
You can use dynamic component and :is prop with vue-fragment when you need a root-less component, or directly vue-fragment if that's just what you need.
Another option is to manipulate the DOM directly.
No, you can't do it. The first thing came to my mind is to move your content in separate component to avoid code duplicating and do something like this
<wrapper v-if="condition">
<child />
</wrapper>
<child v-else />
If you provide some details on why do you need this, probably we can find a better solution.
2022 Working solution:
Create a universal "template wrapper" component:
<!-- VTemplate.vue -->
<template>
<slot></slot>
</template>
Use it in pair with the <component> component:
<script setup>
import VTemplate from './VTemplate.vue'
</script>
<component :is="condition ? 'li' : VTemplate">Your content</component>
<!-- Where 'li' is your wrapper tag/component that you want to conditionally hide but not its content -->
Bonus:
<component :is="condition ? 'li' : VTemplate" :class={'list-item': condition}>
Your content
</component>
<!-- You can assign attributes like the class to it as well -->
P.s. Also, if anyone knows how to import the <template> component directly or at least how to use it in <component :is> please enlighten :)

Include component from parent app in component contained in node_modules directory

I am working on Vue app that incorporates Vue Bootstrap Calendar, and I would like to be able to override the content of the day cell (handled by the Day.vue component) to add my own custom content inside. My thought was initially to modify the Day component to include <slot></slot> tags and pass in the custom content that way.
The problem has to do with accessing the Day component. To include the calendar in your app, you include the Calendar.vue component, which includes Week.vue, which in turn includes Day.vue. As I understand slots, I have to have the child component (Day.vue in this case) included in the component where I'm passing the data, which means it would need to be included in my own component.
If this is not possible, my other thought is to perhaps modify the library by adding another configuration prop (something like dayCustomContent) to the Calendar.vue that indicates that the Day cell content is custom content, pass that in to Calendar.vue, and then down to Day.vue, and then in the Day.vue template, have a v-if conditional based on this prop that either displays the custom content or the default cell content, something like:
<template>
<div class="day-cell" v-if="dayCustomContent">
...my custom content here...
</div>
<div class="day-cell" v-else>
...default events from my.events goes here...
</div>
</template>
I would probably then need to define a custom component to render whatever custom content I want to display, and somehow include that component within Day.vue.
So to sum up, my questions are these:
1) Is there a way to do what I need with slots?
2) For my second option, am I going down the right path? I'm open to suggestions.
UPDATE: I was able to get this done by adding a boolean customDayContent prop in Calendar.vue like so and passing it down to Week.vue and then to Day.vue:
<template>
...
<div class="dates" ref="dates">
<Week
v-for="(week, index) in Weeks"
:firstDay="firstDay"
:key="week + index"
:week="week"
:canAddEvent="canAddEvent"
:canDeleteEvent="canDeleteEvent"
:customDayContent="customDayContent"
:displayWeekNumber="displayWeekNumber"
#eventAdded="eventAdded"
#eventDeleted="eventDeleted"
></Week>
</div>
...
</template>
<script>
export default {
...
props: {
...
customDayContent: {
type: Boolean,
default: false
}
},
}
</script>
and then in Day.vue, do like I had suggested with v-if:
<template>
<div class="day-cell" v-if="customDayContent">
<custom-day></custom-day>
</div>
<div
class="day-cell"
:class="{'today' : day.isToday, 'current-month' : day.isCurrentMonth, 'weekend': day.isWeekEnd, 'selected-day':isDaySelected}"
#click="showDayOptions"
v-else
>
... existing code goes here...
</div>
</template>
The last part is referencing the CustomDay.vue component referenced in my v-if block. I want the user to be able to define the content of their own custom CustomDay.vue template in their own parent app. However, I'm having trouble trying to figure out how to do that. Following the pattern of including components already in this component, I added this in the components section of Day.vue:
CustomDay: require("../../../../src/Components/CustomDay.vue").default
? require("../../../../src/Components/CustomDay.vue").default
: require("../../../../src/Components/CustomDay.vue")
However, no matter what I try along these lines, I get an error that the relative module was not found. On top of that, I need to add it to the componentsarray only if customDayContent is true. What is the best way to do that? In a watcher or computer property, perhaps? Or another way?

Styled-component not injecting all the different css for handling different props

I would like to know what can potentially cause styled-components to not inject all the neccessary css into a page's header.
I have declared a super simple button in an existing project like so:
const Button = styled.button`
background-color: ${props => props.disabled ? "red" : "blue"}
`
And used it like so:
render() {
return (
<div>
<input value={this.state.value} onChange={this.changeValue} />
<Button disabled={this.state.value === "123"}>Button</Button>
</div>
);
}
The problem is the button will display as expected on load (eg. red) but it doesn't have the blue style when the state changes.
Looking at the generated html, this is caused by the 'blue' style (.hqrbog) not injected into the header
<style type="text/css" data-styled-components="jiLefI" data-styled-components-is-local="true">
/* sc-component-id: sc-bdVaJa */
.sc-bdVaJa {}
.jiLefI{background-color:red;}
</style>
<div data-reactroot="">
<input value="12" class="drop-val">
<button class="sc-bdVaJa hqrbog">Button</button>
</div>
When I put these code in a new app created with create-react-component, everything works. But they just dont work in this existing project.
I'm wondering what can cause styled-component to not inject css insto the header properly? I'm suspecting its something to do with how the webpack or babel is set up but I don't know where to start.
Try to rename your attribute from disabled to something else.

What is an elegant way to initialize multiple react-live components from a static website?

I'm currently creating a react component library that i want to provide as an npm package. I also want to provide a documentation that features fancy live rendering of the available components on e.g. github pages.
For the live editing feature i'm planning to use react-live which provides multiple react components to display a live editor and a preview. See an example from the styled-components docs how this looks like.
The react-live component accepts a string code containing the initial code that should be displayed in the editor and a list of components scope that can be used inside the live editor.
Now i want to use gohugo or a similar static site generator to deploy my documentation. I thought i could maybe provide a <div> inside my static site that has a special class react-live-demo and i will get these containers with document.getElementsByClassName('react-live-demo'), loop over them and render the react-live component into it.
I created a code snippet that shows a little example:
const {LiveProvider, LiveEditor, LiveError, LivePreview} = window['react-live'];
// a random component that i want to render in the live editor
const MyComponent = () => (
<div>
<h1>react live demo</h1>
<p>This is a component from the script tag.</p>
</div>
);
// a wrapper for the react-live editor
const Editor = ({code, scope}) => (
<LiveProvider code={code} scope={scope}>
<div className="live-example row">
<div className="col-xs">
<LiveEditor />
<LiveError />
</div>
<div className="col-xs">
<LivePreview />
</div>
</div>
</LiveProvider>
);
// get all containers that have the initial code as innerHTML
const examples = document.querySelectorAll('script[data-name="react-live"]');
examples.forEach(example => {
// insert a new div before the script tag
const container = document.createElement('div');
example.parentNode.insertBefore(container, example.nextSibling);
// render the editor with the code from the script tag
const code = example.innerHTML.trim();
ReactDOM.render(<Editor code={code} scope={{MyComponent}} />, container);
});
.static-content {
background-color: papayawhip;
}
.live-example {
border: 1px solid orange;
}
.react-live-demo.code {
display: none;
}
.invalid {
color: red;
}
<p class="static-content">HERE IS SOME STATIC HTML CONTENT</p>
<script type="text/html" data-name="react-live">
<div>
<h1>react live demo</h1>
<p>This code is editable.</p>
</div>
</script>
<p class="static-content">SOME MORE STATIC HTML CONTENT</p>
<script type="text/html" data-name="react-live">
<MyComponent />
</script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/flexboxgrid/6.3.1/flexboxgrid.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://unpkg.com/react-live#1.7.0/dist/react-live.min.js"></script>
The question is now:
How do embed the code string into my static site so that i can grab it with my js code and pass it to my react-live component?
I thought about <pre> or something like that. Or should i just embed it as inner html and read that? But then it would be displayed for short when the react component hasn't rendered yet. Or should i use some sort of script tag and make it available as globals? The key goal is to make it as easy as possible to add live editing examples to the documentation without touching any javascript code.
However if i pass something like <MyComponent /> as innerHTML this does of course not work as you can see in the code snippet.
Any best practice for that use case appreciated.
EDIT:
Based on the suggestion of #Daniel Alexandrov i edited the code snippet. The solution now reads the innerHTML of script tags with type="text/html set and creates a <div> container to insert the editor. This seems to work quite well. Any more ideas?
In my opinion the best choice is to use the <script> tag with a custom type attribute. Take a look at the Knockout.js template binding which uses type="text/html or the Angular <script type="text/ng-template">
This way the browser will ignore the script tag completely, because it doesn't know how to interpret it.

How to avoid extra wrapping <div> in React?

Today I have started learning ReactJS and after an hour faced with the problem..
I want to insert a component which has two rows inside a div on the page.A simplified example of what I am doing below.
I have an html:
<html>
..
<div id="component-placeholder"></div>
..
</html>
Render function like this:
...
render: function() {
return(
<div className="DeadSimpleComponent">
<div className="DeadSimpleComponent__time">10:23:12</div >
<div className="DeadSimpleComponent__date">MONDAY, 2 MARCH 2015</div>
</div>
)
}
....
And below I am calling render:
ReactDOM.render(<DeadSimpleComponent/>, document.getElementById('component-placeholder'));
Generated HTML looks like this:
<html>
..
<div id="component-placeholder">
<div class="DeadSimpleComponent">
<div class="DeadSimpleComponent__time">10:23:12</div>
<div class="DeadSimpleComponent__date">MONDAY, 2 MARCH 2015</div>
</div>
</div>
..
</html>
The problem that I am not a very happy that React forcing me to wrap all in a div "DeadSimpleComponent". What is the best and simple workaround for it, without explicit DOM manipulations?
UPDATE 7/28/2017: Maintainers of React added that possibility in React 16 Beta 1
Since React 16.2, you can do this:
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
}
This requirement was removed in React version (16.0), so now you are able to avoid that wrapper.
You can use React.Fragment to render a list of elements without creating a parent node, official example:
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}
More here: Fragments
Update 2017-12-05:
React v16.2.0 now fully supports rendering of fragments with improved support for returning multiple children from a components render method without specifying keys in children:
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
}
If you are using a React version prior to v16.2.0, it is also possible to use <React.Fragment>...</React.Fragment> instead:
render() {
return (
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
);
}
Original:
React v16.0 introduced returning an array of elements in render method without wrapping it in a div: https://reactjs.org/blog/2017/09/26/react-v16.0.html
render() {
// No need to wrap list items in an extra element!
return [
// Don't forget the keys :)
<li key="A">First item</li>,
<li key="B">Second item</li>,
<li key="C">Third item</li>,
];
}
At the moment, a key is required for each element to avoid the key warning but this could be changed in future releases:
In the future, we’ll likely add a special fragment syntax to JSX that
doesn’t require keys.
You can use:
render(){
return (
<React.Fragment>
<div>Some data</div>
<div>Som other data</div>
</React.Fragment>
)
}
For further details refer to this documentation.
Use [], instead of ()'s to wrap the entire return.
render: function() {
return[
<div className="DeadSimpleComponent__time">10:23:12</div >
<div className="DeadSimpleComponent__date">MONDAY, 2 MARCH 2015</div>
]
}
I created a component to wrap child components without a DIV. It's called a shadow wrapper: https://www.npmjs.com/package/react-shadow-wrapper
This is still required, BUT React now make sure to create elements without creating an additional DOM element.
The extra wrapping needed (normally with a parent div) because Reacts createElement method require a type parameter which is either a tag name string (such as 'div' or 'span'), a React component type (a class or a function). But this was before they introduce React Fragment.
Refer this NEW api doc for createElement
React.createElement : Create and return a new React element of the given type. The type argument can be either a tag name string (such as 'div' or 'span'), a React component type (a class or a function), or a React fragment type.
here is the official example, Refer React.Fragment.
render() {
return (
<React.Fragment>
Some text.
<h2>A heading</h2>
</React.Fragment>
);
}
I know this question has been answered, you can of course use React.Fragment which doesn't create a node but let's you group stuff like a div.
Additionally if you want to have fun you can implement (and learn lots of things) a React mode that removes the extra div's and for this I really want to share a great video on how you can do it on the react code base itself.
https://www.youtube.com/watch?v=aS41Y_eyNrU
This is of course not something that you would do in practice but it's a good learning opportunity.
You won't be able to get rid of that div element. React.render() needs to return one valid DOM node.
Here is one way to render "transculent" components:
import React from 'react'
const Show = (props) => {
if (props.if || false) {
return (<React.Fragment>{props.children}</React.Fragment>)
}
return '';
};
----
<Show if={yomama.so.biq}>
<img src="https://yomama.so.biq">
<h3>Yoamama</h3>
<Show>
There is workaround too. The below block code generates fragment without the need of React.Fragment.
return [1,2,3].map(i=>{
if(i===1) return <div key={i}>First item</div>
if(i===2) return <div key={i}>Second item</div>
return <div key={i}>Third item</div>
})

Categories

Resources