Frappe UI

A set of components and utilities for rapid UI development.

Frappe UI components are built using Vue 3 and Tailwind. Along with components,
it also ships with directives and utilities that make UI development easier.

Installation

npm install frappe-ui
# or
yarn add frappe-ui

Now, import the FrappeUI plugin and components in your Vue app’s main.js:

import { createApp } from "vue";
import { FrappeUI, Button } from "frappe-ui";
import App from "./App.vue";
import "./index.css";

let app = createApp(App);
app.use(FrappeUI);
app.component("Button", Button);
app.mount("#app");

In your tailwind.config.js file, include the frappe-ui preset:

module.exports = {
  presets: [
    require('frappe-ui/src/utils/tailwind.config')
  ],
  ...
}

Components

Frappe UI ships with a bunch of components. To use a component, you can import it directly from frappe-ui:

<template>
    <Button>Click me</Button>
</template>
<script>
import { Button } from 'frappe-ui';
export default {
    components: {
        Button
    }
}
</script>

You can also register components on the root app so that you don’t have to import them in every component.

main.js

import { createApp } from "vue";
import { Button, Input } from "frappe-ui";

let app = createApp(App);
app.component("Button", Button);
app.component("Input", Input);
app.mount("#app");

Alert

<Alert title="Info">Your account has been created.</Alert>

Avatar

<Avatar label="John Doe" />
<Avatar label="John Doe" imageURL="https://picsum.photos/200" />

Badge

<Badge>Open</Badge>
<Badge color="green">Completed</Badge>
<Badge color="red">Error</Badge>
<Badge color="yellow">Closed</Badge>
<Badge color="blue">Running</Badge>

Button

<Button>Default</Button>
<Button type="primary">Primary</Button>
<Button type="danger">Danger</Button>
<Button type="white">White</Button>
<Button icon="x" />
<Button icon-left="menu">Menu</Button>
<Button icon-right="external-link">Link</Button>
<Button :loading="true">Loading</Button>

Card

<Card title="Heading" subtitle="Sub text">
    <div class="text-base">Card content</div>
</Card>

Dialog

The Dialog component uses teleport feature and requires #modals to exist.
Make sure you add a <div id="modals"></div> before the end of your body tag.

<Button @click="dialogOpen = true">Open Dialog</Button>
<Dialog title="This is Dialog" v-model="dialogOpen">
    <div class="text-base">Dialog content</div>
</Dialog>

Dropdown

The Dropdown component uses teleport feature and requires #popovers to exist.
Make sure you add a <div id="popovers"></div> before the end of your body tag.

<Dropdown :items="[{ label: 'Option 1' }, { label: 'Option 2' }]">
    <template v-slot="{ toggleDropdown }">
        <Button @click="toggleDropdown()">Open Dropdown</Button>
    </template>
</Dropdown>

ErrorMessage

<ErrorMessage message="There was an error" />

FeatherIcon

Uses feather-icons under the hood.

<FeatherIcon class="w-4 h-4" name="menu" />
<FeatherIcon class="w-4 h-4" name="circle" />
<FeatherIcon class="w-4 h-4" name="arrow-left" />
<FeatherIcon class="w-4 h-4" name="arrow-right" />

GreenCheckIcon

<GreenCheckIcon class="w-4 h-4" />

Input

<Input label="Text" type="text" value="" placeholder="Text" />
<Input label="Long Text" type="textarea" value="" placeholder="Textarea" />
<Input
    label="Select"
    type="select"
    value=""
    :options="['Option 1', 'Option 2']"
/>
<Input label="Check" type="checkbox" value="" />

ListItem

<ListItem title="List Item 1" subtitle="Sub text 1">
    <template #actions>
        <Button icon="more-horizontal" />
    </template>
</ListItem>
<ListItem title="List Item 2" subtitle="Sub text 2" />

LoadingIndicator

<LoadingIndicator />

LoadingText

<LoadingText />

Spinner

<Spinner class="w-5" />

SuccessMessage

<SuccessMessage message="Completed successfully" />

Directives

onOutsideClick

This directive is used when you want to execute a function when the user clicks outside of a target element. For e.g., when user clicks outside a dropdown, the dropdown should close.

<button v-on-outside-click="handleOutsideClick">Click me</button>

Utilities

call

This function wraps fetch API. It is built for making web requests to a Frappe server.

call('frappe.client.get_value', {
    doctype: 'ToDo',
    filters: {name: 'adsfasdf'},
    fieldname: 'description'
})

resources

This is a helper for managing async data fetching in Vue apps that work with a Frappe backend.

<template>
    <div>
        <LoadingText v-if="$resources.todos.loading" />
        <div
            v-for="todo in $resources.todos.data || []"
            :key="todo.name"
        >
            <div>{{ todo.description }}</div>
            <Badge>{{ todo.status }}</Badge>
        </div>
        <ErrorMessage message="$resources.todos.error" />
    </div>
</template>
<script>
import { Badge, LoadingText, ErrorMessage } from 'frappe-ui';

export default {
    name: 'ToDos',
    resources: {
        todos: {
            method: 'frappe.client.get_list',
            params: {
                doctype: 'ToDo',
                fields: ['*']
            }
        }
    },
    components: {
        Badge,
        LoadingText,
        ErrorMessage
    }
}
</script>

socketio

This module pre-configures a socketio instance on the port 9000. If you install the FrappeUI plugin, this.$socket will be available in all Vue components.

Usage:

this.$socket.on('list_update', (data) => {
    // handle list update event
});

tailwind.config

This is a tailwind preset that customizes the standard tailwind config to include Frappe design tokens.

Usage:

module.exports = {
  presets: [
    require('frappe-ui/src/utils/tailwind.config')
  ],
  ...
}

Vue Plugin

Vue plugin that installs call, resources and socketio in your Vue app

main.js

import { createApp } from "vue";
import { FrappeUI } from "frappe-ui";
import App from "./App.vue";

let app = createApp(App);
app.use(FrappeUI);
app.mount("#app");

You can now use these features in your Vue components.

<script>
export default {
    resources: {
        ping: 'frappe.handler.ping'
    },
    mounted() {
        this.$call('ping');
        this.$socket.on('list_update', (data) => {
            // handle list update event
        });
    }
}
</script>

License

MIT

GitHub

View Github