Skip to content

Instantly share code, notes, and snippets.

@uriannrima
Last active September 21, 2018 00:27
Show Gist options
  • Save uriannrima/1f0ab3bcb59943638dde014d49c3bcf3 to your computer and use it in GitHub Desktop.
Save uriannrima/1f0ab3bcb59943638dde014d49c3bcf3 to your computer and use it in GitHub Desktop.
<template>
<div class="Sandbox">
<div>
<span>Default:</span>
<themed-button>Main</themed-button>
<themed-button primary>Primary</themed-button>
<span>Themed:</span>
<theme-provider :theme="coloredTheme">
<themed-button @click="$emit('main-button')">Main</themed-button>
<themed-button primary
@click="$emit('primary-button')">Primary</themed-button>
</theme-provider>
<input v-model="coloredTheme.main">
<input v-model="coloredTheme.primary">
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { ThemeProvider } from './ThemeProvider';
import ThemedButton from './TButton.vue';
export default Vue.extend({
components: { ThemeProvider, ThemedButton },
data() {
return {
coloredTheme: {
main: 'purple',
primary: 'gray'
}
};
}
});
</script>
<script lang="ts">
import styled from 'vue-styled-components';
import { withTheme } from './ThemeProvider';
const buttonProps = {
theme: {
type: Object,
default: () => ({
main: 'palevioletred',
primary: 'orange'
})
},
primary: {
type: Boolean,
default: false
}
};
export default withTheme(styled('div', buttonProps)`
font-size: 1em;
margin: 1em;
padding: 0.25em 1em;
border-radius: 3px;
/* Color the border and text with theme.main */
color: ${(props: any) =>
props.primary ? props.theme.primary : props.theme.main};
border: 2px solid
${(props: any) => (props.primary ? props.theme.primary : props.theme.main)};
`);
</script>
import Vue, { PropOptions } from 'vue'
/**
* Generate a theme provider.
* @param themeProps Property validation for the Theme. Must follow the "default" Vue property declaration.
* For example, the default value is { type:Object, required: true }.
* But an VueType validation can also be used for a better props validation.
*/
export const createThemeProvider = (themeProps: PropOptions = { type: Object, required: true }) => {
return Vue.extend({
/**
* Receive theme as props.
*/
props: {
theme: themeProps
},
/**
* Inject 'Theme' to lower components.
*/
provide() {
return {
theme: this.theme
};
},
/**
* Render slots.
*/
render() {
return <div>{this.$slots.default}</div>;
}
});
}
/**
* Generate a default theme provider.
*/
export const ThemeProvider = createThemeProvider();
/**
* Theme Injector Base.
*/
const ThemeInjectorBase = {
/**
* Inject the theme
*/
inject: {
theme: {
default: undefined
}
}
};
/**
* High-Order-Component that injects theme as a property of the component passed as paramenter.
* @param Component Component that will be wrapped.
*/
export const withTheme = (Component: any) => {
return Vue.extend({
name: `Themed${Component.name}`,
props: Component.props,
...ThemeInjectorBase,
render(h) {
var componentProperties: any = {
attrs: this.$attrs,
on: this.$listeners,
props: this.$props
};
const { theme } = this as any;
if (theme) {
componentProperties = {
...componentProperties,
props: { ...this.$props, theme }
}
}
console.log(componentProperties);
return h(Component, componentProperties, this.$slots.default);
}
})
};
/**
* Slot-scope version of the theme injector.
* Theme will be passed as "theme" inside of the slot-scope.
*/
export const ThemeInjector = Vue.extend({
...ThemeInjectorBase,
render() {
const { theme } = this as any;
return <div>{this.$scopedSlots.default({ theme })}</div>;
}
});
export default ThemeProvider;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment