Inkline - Paper
Unified interface for defining components for Vue and React using a single code base.
Inkline is written and maintained by @alexgrozav.
Homepage
·
Documentation
·
Storybook
·
Playground
·
Issue Tracker
Running locally
- First, install dependencies using
npm install
. - From the root directory, run
npm run test
in the command line.
Setup
- Set an alias in your build system to resolve
@inkline/paper
to either@inkline/paper/vue
or@inkline/paper/react
.
-
@inkline/paper
=>@inkline/paper/vue
-
@inkline/paper
=>@inkline/paper/react
-
Import the common component definition interface from
@inkline/paper
and decide whether you're creating a library forvue
orreact
at build time. -
Configure
tsconfig.json
to use the customh
andFragment
JSX Factory functions.
{
"compilerOptions": {
"jsx": "preserve",
"jsxFactory": "h",
"jsxFragmentFactory": "Fragment"
}
}
Usage
Create element
h(type: string, props: Record<string, any>, children: (VNode | string)[]): VNode
The hoist function h()
is used to create elements.
import { h } from '@inkline/paper';
const type = 'button';
const props = { id: 'button' };
const children = ['Hello world'];
const node = h(type, props, children);
It also serves as a JSX Factory.
import { h } from '@inkline/paper';
const node = <div id="myid">Hello world!</div>
Define component
defineComponent<Props, State>(definition: ComponentDefinition<Props, State>)
The defineComponent()
function is used to set up framework-specific internals and get type annotations.
import { defineComponent, h } from '@inkline/paper';
const Component = defineComponent({
render () {
return h('div');
}
});
import { defineComponent, h } from '@inkline/paper';
const Component = defineComponent({
render () {
return <div />;
}
});
Vue.js
<component />
React.js
<Component />
Render function
defineComponent({ render(state: Props & State, ctx: RenderContext): VNode })
The render()
function is mandatory and is used to return the component markup using hoisting.
import { defineComponent, h } from '@inkline/paper';
const Component = defineComponent({
render () {
return h('button', {}, 'Hello world');
}
});
import { defineComponent, h } from '@inkline/paper';
const Component = defineComponent({
render () {
return <button>Hello world</button>;
}
});
Vue.js
<component />
React.js
<Component />
Setup function
defineComponent({ setup(props: Props, ctx: SetupContext) })
The setup()
function is used to prepare functions.
import { defineComponent, h } from '@inkline/paper';
const Component = defineComponent<{}, { text: string }>({
setup () {
return {
text: "Hello world"
};
},
render (state) {
return h('button', {}, [
state.text
]);
}
});
import { defineComponent, h } from '@inkline/paper';
const Component = defineComponent<{}, { text: string }>({
setup () {
return {
text: "Hello world"
};
},
render (state) {
return <button>{state.text}</button>;
}
});
Vue.js
<component />
React.js
<Component />
Reference variables
ref<Type>(defaultValue: Type)
The ref
variable works similar to the Vue.js ref
. To access or set the value of a reference variable, access or manipulate its value
field directly.
import { defineComponent, ref, h, Ref } from '@inkline/paper';
const Component = defineComponent<{}, { text: Ref<string>, onClick: () => void }>({
setup () {
const text = ref('Hello world');
const onClick = () => {
text.value = 'Bye world';
}
return {
text,
onClick
};
},
render (state) {
return h('button', { onClick: state.onClick }, [
state.text.value
]);
}
});
Vue.js
<component />
React.js
<Component />
Computed variables
computed<Type>(() => Type)
import { defineComponent, ref, h, Ref } from '@inkline/paper';
const Component = defineComponent<{ value: number; }, { double: Ref<number> }>({
setup (props) {
const double = computed(() => props.value * 2);
return {
double
};
},
render (state) {
return h('button', {}, [
state.double.value
]);
}
});
Vue.js
<component />
React.js
<Component />
Provide and Inject
provide<Type>(identifier: string, value: Type)
inject<Type>(identifier: string, defaultValue?: Type): Type
import { defineComponent, ref, h, Ref } from '@inkline/paper';
const identifier = Symbol('identifier');
const Provider = defineComponent<{}, {}>({
setup (props, ctx) {
ctx.provide(identifier, 'value');
return {};
},
render (state, ctx) {
return h('div', {}, [
ctx.slot()
]);
}
});
const Consumer = defineComponent<{}, { value?: string; }>({
setup (props, ctx) {
const value = inject(identifier, 'defaultValue');
return { value };
},
render (state, ctx) {
return h('div', {}, [
`${state.value}`
]);
}
});
Vue.js
<provider>
<consumer />
</provider>
React.js
<Provider>
<Consumer />
</Provider>
Props
defineComponent({ props: ComponentProps<Props> })
Define the props using the props
field, using the same format used in Vue.js.
The setup()
function receives the defined prop values with default as fallback.
import { defineComponent, h } from '@inkline/paper';
const Component = defineComponent<{ text: string }, {}>({
props: {
text: {
type: String,
default: () => 'Hello world'
}
},
render (state) {
return h('button', {}, [
state.text
]);
}
});
Vue.js
<component text="Button" />
React.js
<Component text={"Button"} />
Slots
defineComponent({ slots: string[] })` and `renderContext.slot(slotName)
The slots
array allows you to define multiple slot names for the component. Out of the box, the default
slot is pre-defined.
The slot()
function is available in the render function execution context.
import { defineComponent, h } from '@inkline/paper';
const Component = defineComponent({
slots: ['header', 'footer'],
render (state, ctx) {
return h('div', { class: 'card' }, [
ctx.slot('header'),
ctx.slot(), // Default slot
ctx.slot('footer'),
]);
}
});
Vue.js
<component>
<template #header>Header</template>
Body
<template #footer>Header</template>
</component>
React.js
<Component>
<Component.Header>Header</Component.Header>
Body
<Component.Footer>Header</Component.Footer>
</Component>
Events
defineComponent({ emits: string[] })` and `setupContext.emit(eventName, ...args)
The emits
array allows you to define event emitters.
- for Vue.js, this uses the native
emit()
function - for React.js, this creates an
on[EventName]
callback
import { defineComponent, h } from '@inkline/paper';
const Component = defineComponent<{}, { emitChange: () => void }>({
emits: ['change'],
setup (props, ctx) {
const emitChange = () => {
ctx.emit('change');
}
return { emitChange };
},
render (state, ctx) {
return h('button', { onClick: state.emitChange }, [ctx.slot()]);
}
});
Vue.js
<component @change="doSomething" />
React.js
<Component onChange={() => doSomething()} />
Creator
Alex Grozav
- https://grozav.com
- https://twitter.com/alexgrozav
- https://facebook.com/alexgrozav
- https://github.com/alexgrozav
If you use Inkline in your daily work and feel that it has made your life easier, please consider sponsoring me on Github Sponsors.
Copyright and license
Homepage and documentation copyright 2017-2022 Inkline Authors. Docs released under ISC License.