frappe-ui 0.0.88 → 0.0.90

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "name": "frappe-ui",
3
- "version": "0.0.88",
3
+ "version": "0.0.90",
4
4
  "description": "A set of components and utilities for rapid UI development",
5
5
  "main": "./src/index.js",
6
6
  "scripts": {
7
7
  "test": "npx prettier --check ./src",
8
8
  "prettier": "npx prettier -w ./src",
9
9
  "prepare": "husky install",
10
- "bump-and-release": "git pull --rebase origin main && yarn version --patch && git push && git push --tags"
10
+ "bump-and-release": "git pull --rebase origin main && yarn version --patch && git push && git push --tags",
11
+ "docs:dev": "vitepress dev docs",
12
+ "docs:build": "vitepress build docs",
13
+ "docs:serve": "vitepress serve docs"
11
14
  },
12
15
  "files": [
13
16
  "src"
@@ -35,6 +38,7 @@
35
38
  "@tiptap/extension-table-row": "^2.0.0-beta.22",
36
39
  "@tiptap/extension-text-align": "^2.0.0-beta.31",
37
40
  "@tiptap/extension-text-style": "^2.0.0-beta.202",
41
+ "@tiptap/extension-typography": "^2.0.0-beta.202",
38
42
  "@tiptap/starter-kit": "^2.0.0-beta.191",
39
43
  "@tiptap/suggestion": "^2.0.0-beta.195",
40
44
  "@tiptap/vue-3": "^2.0.0-beta.96",
@@ -48,10 +52,13 @@
48
52
  "tippy.js": "^6.3.7"
49
53
  },
50
54
  "devDependencies": {
55
+ "cross-fetch": "^3.1.5",
51
56
  "husky": ">=6",
52
57
  "lint-staged": ">=10",
53
58
  "prettier": "2.7.1",
54
- "prettier-plugin-tailwindcss": "^0.1.13"
59
+ "prettier-plugin-tailwindcss": "^0.1.13",
60
+ "vitepress": "^1.0.0-alpha.29",
61
+ "vue": "^3.2.45"
55
62
  },
56
63
  "lint-staged": {
57
64
  "*.{js,css,md,vue}": "prettier --write"
package/readme.md CHANGED
@@ -1,16 +1,27 @@
1
- # Frappe UI
1
+ <p align="center">
2
+ <a href="https://github.com/frappe/frappe-ui">
3
+ <img src="./docs/public/frappe-ui-logo.svg" width="250" />
4
+ </a>
5
+ </p>
6
+ <h1 style="font-size: 24px" align="center">Rapidly build modern frontends for Frappe apps</h1>
2
7
 
3
- A set of components and utilities for rapid UI development.
8
+ <p align="center">
9
+ <img alt="MIT License" src="https://img.shields.io/github/license/frappe/frappe-ui"/>
10
+ <img alt="NPM Downloads" src="https://img.shields.io/npm/dm/frappe-ui.svg?style=flat"/>
11
+ </p>
4
12
 
5
- Frappe UI components are built using Vue 3 and Tailwind. Along with components,
6
- it also ships with directives and utilities that make UI development easier.
13
+ Frappe UI provides a set of components and utilities for rapid UI development.
14
+ Components are built using Vue 3 and Tailwind. Along with components, there are
15
+ directives and utilities that make UI development easier.
7
16
 
8
- <details>
9
- <summary>Show components</summary>
10
- <img src="https://user-images.githubusercontent.com/9355208/152111395-a62e1599-112c-450f-a32a-5debc791eb2e.png" width="80%" />
11
- </details>
17
+ ## Links
18
+
19
+ - [Documentation](https://frappeui.com)
20
+ - [Frappe UI Starter Boilerplate](https://github.com/netchampfaris/frappe-ui-starter)
21
+ - [Community](https://github.com/frappe/frappe-ui/discussions)
12
22
 
13
23
  ## Installation
24
+
14
25
  ```sh
15
26
  npm install frappe-ui
16
27
  # or
@@ -20,15 +31,14 @@ yarn add frappe-ui
20
31
  Now, import the FrappeUI plugin and components in your Vue app's `main.js`:
21
32
 
22
33
  ```js
23
- import { createApp } from "vue";
24
- import { FrappeUI, Button } from "frappe-ui";
25
- import App from "./App.vue";
26
- import "./index.css";
34
+ import { createApp } from 'vue'
35
+ import { FrappeUI } from 'frappe-ui'
36
+ import App from './App.vue'
37
+ import './index.css'
27
38
 
28
- let app = createApp(App);
29
- app.use(FrappeUI);
30
- app.component("Button", Button);
31
- app.mount("#app");
39
+ let app = createApp(App)
40
+ app.use(FrappeUI)
41
+ app.mount('#app')
32
42
  ```
33
43
 
34
44
  In your `tailwind.config.js` file, include the frappe-ui preset:
@@ -42,277 +52,31 @@ module.exports = {
42
52
  }
43
53
  ```
44
54
 
45
- ## Components
46
-
47
- Frappe UI ships with a bunch of components. To use a component, you can import it directly from `frappe-ui`:
48
55
  ```html
49
56
  <template>
50
- <Button>Click me</Button>
57
+ <button>Click me</button>
51
58
  </template>
52
59
  <script>
53
- import { Button } from 'frappe-ui';
54
- export default {
60
+ import { Button } from 'frappe-ui'
61
+ export default {
55
62
  components: {
56
- Button
57
- }
58
- }
59
- </script>
60
- ```
61
-
62
- You can also register components on the root `app` so that you don't have to import them in every component.
63
-
64
- `main.js`
65
- ```js
66
- import { createApp } from "vue";
67
- import { Button, Input } from "frappe-ui";
68
-
69
- let app = createApp(App);
70
- app.component("Button", Button);
71
- app.component("Input", Input);
72
- app.mount("#app");
73
- ```
74
-
75
- ### Alert
76
- ```html
77
- <Alert title="Info">Your account has been created.</Alert>
78
- ```
79
-
80
- ### Avatar
81
- ```html
82
- <Avatar label="John Doe" />
83
- <Avatar label="John Doe" imageURL="https://picsum.photos/200" />
84
- ```
85
-
86
- ### Badge
87
- ```html
88
- <Badge>Open</Badge>
89
- <Badge color="green">Completed</Badge>
90
- <Badge color="red">Error</Badge>
91
- <Badge color="yellow">Closed</Badge>
92
- <Badge color="blue">Running</Badge>
93
- ```
94
-
95
- ### Button
96
- ```html
97
- <Button>Default</Button>
98
- <Button type="primary">Primary</Button>
99
- <Button type="danger">Danger</Button>
100
- <Button type="white">White</Button>
101
- <Button icon="x" />
102
- <Button icon-left="menu">Menu</Button>
103
- <Button icon-right="external-link">Link</Button>
104
- <Button :loading="true">Loading</Button>
105
- ```
106
-
107
- ### Card
108
- ```html
109
- <Card title="Heading" subtitle="Sub text">
110
- <div class="text-base">Card content</div>
111
- </Card>
112
- ```
113
-
114
- ### Dialog
115
- The Dialog component uses `teleport` feature and requires `#modals` to exist.
116
- Make sure you add a `<div id="modals"></div>` before the end of your body tag.
117
-
118
- ```html
119
- <Button @click="dialogOpen = true">Open Dialog</Button>
120
- <Dialog title="This is Dialog" v-model="dialogOpen">
121
- <div class="text-base">Dialog content</div>
122
- </Dialog>
123
- ```
124
-
125
- ### Dropdown
126
- The Dropdown component uses `teleport` feature and requires `#popovers` to exist.
127
- Make sure you add a `<div id="popovers"></div>` before the end of your body tag.
128
-
129
- ```html
130
- <Dropdown :items="[{ label: 'Option 1' }, { label: 'Option 2' }]">
131
- <template v-slot="{ toggleDropdown }">
132
- <Button @click="toggleDropdown()">Open Dropdown</Button>
133
- </template>
134
- </Dropdown>
135
- ```
136
-
137
- ### ErrorMessage
138
- ```html
139
- <ErrorMessage message="There was an error" />
140
- ```
141
-
142
- ### FeatherIcon
143
- Uses [`feather-icons`](https://github.com/feathericons/feather) under the hood.
144
-
145
- ```html
146
- <FeatherIcon class="w-4 h-4" name="menu" />
147
- <FeatherIcon class="w-4 h-4" name="circle" />
148
- <FeatherIcon class="w-4 h-4" name="arrow-left" />
149
- <FeatherIcon class="w-4 h-4" name="arrow-right" />
150
- ```
151
-
152
- ### GreenCheckIcon
153
- ```html
154
- <GreenCheckIcon class="w-4 h-4" />
155
- ```
156
-
157
- ### Input
158
- ```html
159
- <Input label="Text" type="text" value="" placeholder="Text" />
160
- <Input label="Long Text" type="textarea" value="" placeholder="Textarea" />
161
- <Input
162
- label="Select"
163
- type="select"
164
- value=""
165
- :options="['Option 1', 'Option 2']"
166
- />
167
- <Input label="Check" type="checkbox" value="" />
168
- ```
169
-
170
- ### ListItem
171
- ```html
172
- <ListItem title="List Item 1" subtitle="Sub text 1">
173
- <template #actions>
174
- <Button icon="more-horizontal" />
175
- </template>
176
- </ListItem>
177
- <ListItem title="List Item 2" subtitle="Sub text 2" />
178
- ```
179
-
180
- ### LoadingIndicator
181
- ```html
182
- <LoadingIndicator />
183
- ```
184
-
185
- ### LoadingText
186
- ```html
187
- <LoadingText />
188
- ```
189
-
190
- ### Spinner
191
- ```html
192
- <Spinner class="w-5" />
193
- ```
194
-
195
- ### SuccessMessage
196
- ```html
197
- <SuccessMessage message="Completed successfully" />
198
- ```
199
-
200
- ## Directives
201
-
202
- ### onOutsideClick
203
- 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.
204
-
205
- ```html
206
- <button v-on-outside-click="handleOutsideClick">Click me</button>
207
- ```
208
-
209
- ## Utilities
210
-
211
- ### call
212
- This function wraps `fetch` API. It is built for making web requests to a Frappe server.
213
-
214
- ```js
215
- call('frappe.client.get_value', {
216
- doctype: 'ToDo',
217
- filters: {name: 'adsfasdf'},
218
- fieldname: 'description'
219
- })
220
- ```
221
-
222
- ### resources
223
- This is a helper for managing async data fetching in Vue apps that work with a Frappe backend.
224
-
225
- ```html
226
- <template>
227
- <div>
228
- <LoadingText v-if="$resources.todos.loading" />
229
- <div
230
- v-for="todo in $resources.todos.data || []"
231
- :key="todo.name"
232
- >
233
- <div>{{ todo.description }}</div>
234
- <Badge>{{ todo.status }}</Badge>
235
- </div>
236
- <ErrorMessage message="$resources.todos.error" />
237
- </div>
238
- </template>
239
- <script>
240
- import { Badge, LoadingText, ErrorMessage } from 'frappe-ui';
241
-
242
- export default {
243
- name: 'ToDos',
244
- resources: {
245
- todos: {
246
- method: 'frappe.client.get_list',
247
- params: {
248
- doctype: 'ToDo',
249
- fields: ['*']
250
- }
251
- }
63
+ Button,
252
64
  },
253
- components: {
254
- Badge,
255
- LoadingText,
256
- ErrorMessage
257
- }
258
- }
65
+ }
259
66
  </script>
260
67
  ```
261
68
 
262
- ### socketio
263
-
264
- 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.
265
-
266
- Usage:
267
- ```js
268
- this.$socket.on('list_update', (data) => {
269
- // handle list update event
270
- });
271
- ```
272
-
273
- ### tailwind.config
274
- This is a [tailwind preset](https://tailwindcss.com/docs/presets) that customizes the standard tailwind config to include Frappe design tokens.
275
-
276
- Usage:
277
- ```js
278
- module.exports = {
279
- presets: [
280
- require('frappe-ui/src/utils/tailwind.config')
281
- ],
282
- ...
283
- }
284
- ```
285
-
286
- ## Vue Plugin
287
- Vue plugin that installs call, resources and socketio in your Vue app
69
+ ## Used By
288
70
 
289
- `main.js`
290
- ```js
291
- import { createApp } from "vue";
292
- import { FrappeUI } from "frappe-ui";
293
- import App from "./App.vue";
71
+ Frappe UI is being used in a lot of products by
72
+ [Frappe](https://github.com/frappe).
294
73
 
295
- let app = createApp(App);
296
- app.use(FrappeUI);
297
- app.mount("#app");
298
- ```
299
-
300
- You can now use these features in your Vue components.
301
- ```html
302
- <script>
303
- export default {
304
- resources: {
305
- ping: 'frappe.handler.ping'
306
- },
307
- mounted() {
308
- this.$call('ping');
309
- this.$socket.on('list_update', (data) => {
310
- // handle list update event
311
- });
312
- }
313
- }
314
- </script>
315
- ```
74
+ - [Frappe Cloud](https://frappecloud.com)
75
+ - [Gameplan](https://github.com/frappe/gameplan)
76
+ - [Frappe Desk](https://frappedesk.com)
77
+ - [Frappe Insights](https://github.com/frappe/insights)
78
+ - [Frappe Drive](https://github.com/frappe/drive)
316
79
 
317
80
  ## License
81
+
318
82
  MIT
@@ -6,11 +6,7 @@
6
6
  <ComboboxButton
7
7
  class="flex w-full items-center justify-between rounded-md bg-gray-100 py-1.5 pl-3 pr-2"
8
8
  :class="{ 'rounded-b-none': isComboboxOpen }"
9
- @click="
10
- () => {
11
- openPopover()
12
- }
13
- "
9
+ @click="() => openPopover()"
14
10
  >
15
11
  <span
16
12
  class="overflow-hidden text-ellipsis text-base"
@@ -100,6 +96,8 @@ import {
100
96
  ComboboxButton,
101
97
  } from '@headlessui/vue'
102
98
  import Popover from './Popover.vue'
99
+ import Button from './Button.vue'
100
+ import FeatherIcon from './FeatherIcon.vue'
103
101
 
104
102
  export default {
105
103
  name: 'Autocomplete',
@@ -107,6 +105,8 @@ export default {
107
105
  emits: ['update:modelValue', 'change'],
108
106
  components: {
109
107
  Popover,
108
+ Button,
109
+ FeatherIcon,
110
110
  Combobox,
111
111
  ComboboxInput,
112
112
  ComboboxOptions,
@@ -3,26 +3,14 @@
3
3
  class="inline-block cursor-default rounded-md px-3 py-1 text-xs font-medium"
4
4
  :class="classes"
5
5
  >
6
- <slot>{{ status }}</slot>
6
+ <slot>{{ label }}</slot>
7
7
  </span>
8
8
  </template>
9
9
  <script>
10
- const DEFAULT_COLOR_MAP = {
11
- Pending: 'yellow',
12
- Running: 'yellow',
13
- Success: 'green',
14
- Failure: 'red',
15
- Active: 'green',
16
- Broken: 'red',
17
- Updating: 'blue',
18
- Rejected: 'red',
19
- Published: 'green',
20
- Approved: 'green',
21
- }
22
-
10
+ let validColors = ['gray', 'red', 'yellow', 'green', 'blue']
23
11
  export default {
24
12
  name: 'Badge',
25
- props: ['color', 'status', 'colorMap'],
13
+ props: ['color', 'label', 'colorMap'],
26
14
  computed: {
27
15
  classes() {
28
16
  let color = this.getBadgeColor()
@@ -41,13 +29,12 @@ export default {
41
29
  methods: {
42
30
  getBadgeColor() {
43
31
  let color = this.color
44
- if (color) {
45
- return color
32
+ if (this.colorMap) {
33
+ color = this.colorMap[this.label]
34
+ }
35
+ if (!color || !validColors.includes(color)) {
36
+ color = 'gray'
46
37
  }
47
-
48
- let statusColorMap = Object.assign(DEFAULT_COLOR_MAP, this.colorMap || {})
49
- color = statusColorMap[this.status] || 'gray'
50
-
51
38
  return color
52
39
  },
53
40
  },
@@ -3,6 +3,7 @@
3
3
  <template #target="{ togglePopover }">
4
4
  <Input
5
5
  type="text"
6
+ icon-left="calendar"
6
7
  :class="inputClass"
7
8
  :value="
8
9
  modelValue && formatValue ? formatValue(modelValue) : modelValue || ''
@@ -92,6 +93,8 @@
92
93
  </template>
93
94
 
94
95
  <script>
96
+ import Input from './Input.vue'
97
+ import FeatherIcon from './FeatherIcon.vue'
95
98
  import Popover from './Popover.vue'
96
99
 
97
100
  export default {
@@ -99,6 +102,8 @@ export default {
99
102
  props: ['modelValue', 'placeholder', 'readonly', 'formatValue', 'inputClass'],
100
103
  emits: ['update:modelValue'],
101
104
  components: {
105
+ Input,
106
+ FeatherIcon,
102
107
  Popover,
103
108
  },
104
109
  data() {
@@ -46,8 +46,8 @@
46
46
  'max-w-3xl': options.size === '3xl',
47
47
  'max-w-2xl': options.size === '2xl',
48
48
  'max-w-xl': options.size === 'xl',
49
- 'max-w-md': options.size === 'md',
50
49
  'max-w-lg': options.size === 'lg' || !options.size,
50
+ 'max-w-md': options.size === 'md',
51
51
  'max-w-sm': options.size === 'sm',
52
52
  'max-w-xs': options.size === 'xs',
53
53
  }"
@@ -60,6 +60,7 @@
60
60
  v-if="icon"
61
61
  class="mx-auto mb-3 flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full sm:mx-0 sm:mb-0 sm:mr-4 sm:h-9 sm:w-9"
62
62
  :class="{
63
+ 'bg-gray-100': !icon.appearance,
63
64
  'bg-yellow-100': icon.appearance === 'warning',
64
65
  'bg-blue-100': icon.appearance === 'info',
65
66
  'bg-red-100': icon.appearance === 'danger',
@@ -68,8 +69,9 @@
68
69
  >
69
70
  <FeatherIcon
70
71
  :name="icon.name"
71
- class="h-6 w-6 text-red-600 sm:h-5 sm:w-5"
72
+ class="h-6 w-6 sm:h-5 sm:w-5"
72
73
  :class="{
74
+ 'text-gray-600': !icon.appearance,
73
75
  'text-yellow-600': icon.appearance === 'warning',
74
76
  'text-blue-600': icon.appearance === 'info',
75
77
  'text-red-600': icon.appearance === 'danger',
@@ -198,10 +200,7 @@ export default {
198
200
 
199
201
  let icon = this.options.icon
200
202
  if (typeof icon === 'string') {
201
- icon = {
202
- name: icon,
203
- type: 'info',
204
- }
203
+ icon = { name: icon }
205
204
  }
206
205
  return icon
207
206
  },
@@ -64,16 +64,31 @@
64
64
 
65
65
  <script>
66
66
  import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue'
67
+ import Button from './Button.vue'
67
68
  import FeatherIcon from './FeatherIcon.vue'
68
69
 
69
70
  export default {
70
71
  name: 'NewDropdown',
71
- props: ['button', 'options', 'placement'],
72
+ props: {
73
+ button: {
74
+ type: Object,
75
+ default: null,
76
+ },
77
+ options: {
78
+ type: Array,
79
+ default: () => [],
80
+ },
81
+ placement: {
82
+ type: String,
83
+ default: 'left',
84
+ },
85
+ },
72
86
  components: {
73
87
  Menu,
74
88
  MenuButton,
75
89
  MenuItems,
76
90
  MenuItem,
91
+ Button,
77
92
  FeatherIcon,
78
93
  },
79
94
  methods: {
@@ -130,7 +130,7 @@ class FileUploader {
130
130
 
131
131
  export default {
132
132
  name: 'FileUploader',
133
- props: ['fileTypes', 'uploadArgs', 'type', 'validateFile'],
133
+ props: ['fileTypes', 'uploadArgs', 'validateFile'],
134
134
  data() {
135
135
  return {
136
136
  uploader: null,
@@ -204,11 +204,11 @@ export default {
204
204
  .catch((error) => {
205
205
  this.uploading = false
206
206
  let errorMessage = 'Error Uploading File'
207
- if (error._server_messages) {
207
+ if (error?._server_messages) {
208
208
  errorMessage = JSON.parse(
209
209
  JSON.parse(error._server_messages)[0]
210
210
  ).message
211
- } else if (error.exc) {
211
+ } else if (error?.exc) {
212
212
  errorMessage = JSON.parse(error.exc)[0].split('\n').slice(-2, -1)[0]
213
213
  }
214
214
  this.error = errorMessage
@@ -49,7 +49,7 @@
49
49
  ref="input"
50
50
  :value="passedInputValue"
51
51
  :disabled="disabled"
52
- :rows="rows || 3"
52
+ :rows="rows"
53
53
  ></textarea>
54
54
  <select
55
55
  v-if="type === 'select'"
@@ -129,6 +129,7 @@ export default {
129
129
  },
130
130
  rows: {
131
131
  type: Number,
132
+ default: 3,
132
133
  },
133
134
  placeholder: {
134
135
  type: String,
@@ -1,12 +1,27 @@
1
1
  <template>
2
- <Spinner class="max-w-xs" />
2
+ <svg
3
+ class="max-w-xs animate-spin"
4
+ xmlns="http://www.w3.org/2000/svg"
5
+ fill="none"
6
+ viewBox="0 0 24 24"
7
+ >
8
+ <circle
9
+ class="opacity-25"
10
+ cx="12"
11
+ cy="12"
12
+ r="10"
13
+ stroke="currentColor"
14
+ stroke-width="4"
15
+ ></circle>
16
+ <path
17
+ class="opacity-75"
18
+ fill="currentColor"
19
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
20
+ ></path>
21
+ </svg>
3
22
  </template>
4
23
  <script>
5
- import Spinner from './Spinner.vue'
6
24
  export default {
7
25
  name: 'LoadingIndicator',
8
- components: {
9
- Spinner,
10
- },
11
26
  }
12
27
  </script>