pimelon-ui 0.0.57 → 0.0.73

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,13 @@
1
1
  {
2
2
  "name": "pimelon-ui",
3
- "version": "0.0.57",
3
+ "version": "0.0.73",
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": "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
11
  },
12
12
  "files": [
13
13
  "src"
@@ -33,9 +33,11 @@
33
33
  "@tiptap/extension-table-row": "^2.0.0-beta.22",
34
34
  "@tiptap/extension-text-align": "^2.0.0-beta.31",
35
35
  "@tiptap/starter-kit": "^2.0.0-beta.191",
36
+ "@tiptap/suggestion": "^2.0.0-beta.195",
36
37
  "@tiptap/vue-3": "^2.0.0-beta.96",
37
38
  "autoprefixer": "^10.4.2",
38
39
  "feather-icons": "^4.28.0",
40
+ "idb-keyval": "^6.2.0",
39
41
  "postcss": "^8.4.5",
40
42
  "socket.io-client": "^4.5.1",
41
43
  "tailwindcss": "^3.0.12",
@@ -31,7 +31,7 @@
31
31
  </template>
32
32
  <template #body>
33
33
  <ComboboxOptions
34
- class="max-h-[11rem] overflow-y-auto rounded-md rounded-t-none bg-white px-1.5 pb-1.5 shadow-md"
34
+ class="max-h-[15rem] overflow-y-auto rounded-md rounded-t-none bg-white px-1.5 pb-1.5 shadow-md"
35
35
  static
36
36
  v-show="isComboboxOpen"
37
37
  >
@@ -52,24 +52,36 @@
52
52
  />
53
53
  <Button icon="x" @click="selectedValue = null" />
54
54
  </div>
55
- <ComboboxOption
56
- as="template"
57
- v-for="option in filteredOptions"
58
- :key="option.value"
59
- :value="option"
60
- v-slot="{ active, selected }"
55
+ <div
56
+ v-for="group in groups"
57
+ :key="group.key"
58
+ v-show="group.items.length > 0"
61
59
  >
62
- <li
63
- :class="[
64
- 'rounded-md px-2.5 py-1.5 text-base',
65
- { 'bg-gray-100': active },
66
- ]"
60
+ <div
61
+ v-if="group.group && !group.hideLabel"
62
+ class="px-2 py-1 text-xs font-semibold uppercase tracking-wider text-gray-500"
63
+ >
64
+ {{ group.group }}
65
+ </div>
66
+ <ComboboxOption
67
+ as="template"
68
+ v-for="option in group.items"
69
+ :key="option.value"
70
+ :value="option"
71
+ v-slot="{ active, selected }"
67
72
  >
68
- {{ option.label }}
69
- </li>
70
- </ComboboxOption>
73
+ <li
74
+ :class="[
75
+ 'rounded-md px-2.5 py-1.5 text-base',
76
+ { 'bg-gray-100': active },
77
+ ]"
78
+ >
79
+ {{ option.label }}
80
+ </li>
81
+ </ComboboxOption>
82
+ </div>
71
83
  <li
72
- v-if="filteredOptions.length == 0"
84
+ v-if="groups.length == 0"
73
85
  class="rounded-md px-2.5 py-1.5 text-base text-gray-600"
74
86
  >
75
87
  No results found
@@ -119,11 +131,31 @@ export default {
119
131
  this.$emit(this.valuePropPassed ? 'change' : 'update:modelValue', val)
120
132
  },
121
133
  },
122
- filteredOptions() {
134
+ groups() {
135
+ if (!this.options || this.options.length == 0) return []
136
+
137
+ let groups = this.options[0]?.group
138
+ ? this.options
139
+ : [{ group: '', items: this.options }]
140
+
141
+ return groups
142
+ .map((group, i) => {
143
+ return {
144
+ key: i,
145
+ group: group.group,
146
+ hideLabel: group.hideLabel || false,
147
+ items: this.filterOptions(group.items),
148
+ }
149
+ })
150
+ .filter((group) => group.items.length > 0)
151
+ },
152
+ },
153
+ methods: {
154
+ filterOptions(options) {
123
155
  if (!this.query) {
124
- return this.options
156
+ return options
125
157
  }
126
- return this.options.filter((option) => {
158
+ return options.filter((option) => {
127
159
  let searchTexts = [option.label, option.value]
128
160
  return searchTexts.some((text) =>
129
161
  (text || '')
@@ -133,8 +165,6 @@ export default {
133
165
  )
134
166
  })
135
167
  },
136
- },
137
- methods: {
138
168
  displayValue(option) {
139
169
  if (typeof option === 'string') {
140
170
  return option
@@ -5,6 +5,7 @@
5
5
  :src="imageURL"
6
6
  class="object-cover"
7
7
  :class="styleClasses"
8
+ loading="lazy"
8
9
  />
9
10
  <div
10
11
  v-else
@@ -7,29 +7,28 @@
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
23
  export default {
11
24
  name: 'Badge',
12
25
  props: ['color', 'status', 'colorMap'],
13
- data: {
14
- defaultColorMap: {
15
- Pending: 'yellow',
16
- Running: 'yellow',
17
- Success: 'green',
18
- Failure: 'red',
19
- Active: 'green',
20
- Broken: 'red',
21
- Updating: 'blue',
22
- Rejected: 'red',
23
- Published: 'green',
24
- Approved: 'green',
25
- },
26
- },
27
26
  computed: {
28
27
  classes() {
29
28
  let color = this.getBadgeColor()
30
29
 
31
30
  let cssClasses = {
32
- gray: 'text-gray-700 bg-gray-50',
31
+ gray: 'text-gray-700 bg-gray-100',
33
32
  green: 'text-green-700 bg-green-50',
34
33
  red: 'text-red-700 bg-red-50',
35
34
  yellow: 'text-yellow-700 bg-yellow-50',
@@ -46,10 +45,7 @@ export default {
46
45
  return color
47
46
  }
48
47
 
49
- let statusColorMap = Object.assign(
50
- this.defaultColorMap,
51
- this.colorMap || {}
52
- )
48
+ let statusColorMap = Object.assign(DEFAULT_COLOR_MAP, this.colorMap || {})
53
49
  color = statusColorMap[this.status] || 'gray'
54
50
 
55
51
  return color
@@ -42,15 +42,18 @@
42
42
  ]"
43
43
  @click="item.onClick"
44
44
  >
45
- <FeatherIcon
46
- v-if="item.icon"
47
- :name="item.icon"
48
- class="mr-2 h-4 w-4 flex-shrink-0 text-gray-500"
49
- aria-hidden="true"
50
- />
51
- <span class="whitespace-nowrap">
52
- {{ item.label }}
53
- </span>
45
+ <component :is="item.component" v-if="item.component" />
46
+ <template v-else>
47
+ <FeatherIcon
48
+ v-if="item.icon"
49
+ :name="item.icon"
50
+ class="mr-2 h-4 w-4 flex-shrink-0 text-gray-500"
51
+ aria-hidden="true"
52
+ />
53
+ <span class="whitespace-nowrap">
54
+ {{ item.label }}
55
+ </span>
56
+ </template>
54
57
  </button>
55
58
  </MenuItem>
56
59
  </div>
@@ -79,10 +82,12 @@ export default {
79
82
  if (!onClick && option.route && this.$router) {
80
83
  onClick = () => this.$router.push(option.route)
81
84
  }
85
+
82
86
  return {
83
87
  label: option.label,
84
88
  icon: option.icon,
85
89
  group: option.group,
90
+ component: option.component,
86
91
  onClick,
87
92
  }
88
93
  },
@@ -3,7 +3,7 @@
3
3
  v-if="message"
4
4
  class="whitespace-pre-line text-sm text-red-600"
5
5
  role="alert"
6
- v-html="message"
6
+ v-html="errorMessage"
7
7
  ></div>
8
8
  </template>
9
9
 
@@ -11,5 +11,14 @@
11
11
  export default {
12
12
  name: 'ErrorMessage',
13
13
  props: ['message'],
14
+ computed: {
15
+ errorMessage() {
16
+ if (!this.message) return ''
17
+ if (this.message instanceof Error) {
18
+ return this.message.messages || this.message.message
19
+ }
20
+ return this.message
21
+ },
22
+ },
14
23
  }
15
24
  </script>
@@ -6,55 +6,70 @@
6
6
  >
7
7
  {{ label }}
8
8
  </span>
9
- <input
10
- v-if="
11
- ['text', 'number', 'checkbox', 'email', 'password', 'date'].includes(
12
- type
13
- )
14
- "
15
- v-bind="inputAttributes"
16
- class="border-gray-400 placeholder-gray-500"
17
- ref="input"
18
- :class="[
19
- {
20
- 'form-input block w-full': type != 'checkbox',
21
- 'form-checkbox': type == 'checkbox',
22
- },
23
- inputClass,
24
- ]"
25
- :type="type || 'text'"
26
- :disabled="disabled"
27
- :placeholder="placeholder"
28
- :value="passedInputValue"
29
- />
30
- <textarea
31
- v-if="type === 'textarea'"
32
- v-bind="inputAttributes"
33
- :placeholder="placeholder"
34
- class="placeholder-gray-500"
35
- :class="['form-textarea block w-full resize-none', inputClass]"
36
- ref="input"
37
- :value="passedInputValue"
38
- :disabled="disabled"
39
- :rows="rows || 3"
40
- ></textarea>
41
- <select
42
- v-bind="inputAttributes"
43
- class="form-select block w-full"
44
- ref="input"
45
- v-if="type === 'select'"
46
- :disabled="disabled"
9
+ <div
10
+ class="relative flex"
11
+ :class="{ 'items-center': isNormalInput || type == 'select' }"
47
12
  >
48
- <option
49
- v-for="option in selectOptions"
50
- :key="option.value"
51
- :value="option.value"
52
- :disabled="option.disabled || false"
53
- :selected="passedInputValue === option.value"
13
+ <FeatherIcon
14
+ v-if="iconLeft && type != 'checkbox'"
15
+ :name="iconLeft"
16
+ class="absolute mx-2 h-4 w-4 text-gray-600"
17
+ :class="{ 'mt-2': type == 'textarea' }"
18
+ />
19
+ <input
20
+ v-if="isNormalInput"
21
+ v-bind="inputAttributes"
22
+ class="border-gray-400 placeholder-gray-500"
23
+ ref="input"
24
+ :class="[
25
+ {
26
+ 'form-input block w-full': type != 'checkbox',
27
+ 'form-checkbox': type == 'checkbox',
28
+ 'pl-8': iconLeft && type != 'checkbox',
29
+ },
30
+ inputClass,
31
+ ]"
32
+ :type="type || 'text'"
33
+ :disabled="disabled"
34
+ :placeholder="placeholder"
35
+ :value="passedInputValue"
36
+ />
37
+ <textarea
38
+ v-if="type === 'textarea'"
39
+ v-bind="inputAttributes"
40
+ :placeholder="placeholder"
41
+ class="placeholder-gray-500"
42
+ :class="[
43
+ 'form-textarea block w-full resize-none',
44
+ inputClass,
45
+ {
46
+ 'pl-8': iconLeft,
47
+ },
48
+ ]"
49
+ ref="input"
50
+ :value="passedInputValue"
51
+ :disabled="disabled"
52
+ :rows="rows || 3"
53
+ ></textarea>
54
+ <select
55
+ v-if="type === 'select'"
56
+ v-bind="inputAttributes"
57
+ class="form-select block w-full"
58
+ :class="{ 'pl-8': iconLeft }"
59
+ ref="input"
60
+ :disabled="disabled"
54
61
  >
55
- {{ option.label }}
56
- </option>
57
- </select>
62
+ <option
63
+ v-for="option in selectOptions"
64
+ :key="option.value"
65
+ :value="option.value"
66
+ :disabled="option.disabled || false"
67
+ :selected="passedInputValue === option.value"
68
+ >
69
+ {{ option.label }}
70
+ </option>
71
+ </select>
72
+ </div>
58
73
  <span
59
74
  v-if="label && type == 'checkbox'"
60
75
  class="ml-2 inline-block text-base leading-4"
@@ -66,11 +81,13 @@
66
81
 
67
82
  <script>
68
83
  import { debounce } from 'pimelon-ui'
84
+ import FeatherIcon from './FeatherIcon.vue'
69
85
 
70
86
  export default {
71
87
  name: 'Input',
72
88
  inheritAttrs: false,
73
89
  expose: ['getInputValue'],
90
+ components: { FeatherIcon },
74
91
  props: {
75
92
  label: {
76
93
  type: String,
@@ -116,6 +133,9 @@ export default {
116
133
  placeholder: {
117
134
  type: String,
118
135
  },
136
+ iconLeft: {
137
+ type: String,
138
+ },
119
139
  },
120
140
  emits: ['input', 'change', 'update:modelValue'],
121
141
  methods: {
@@ -173,6 +193,16 @@ export default {
173
193
  })
174
194
  .filter(Boolean)
175
195
  },
196
+ isNormalInput() {
197
+ return [
198
+ 'text',
199
+ 'number',
200
+ 'checkbox',
201
+ 'email',
202
+ 'password',
203
+ 'date',
204
+ ].includes(this.type)
205
+ },
176
206
  },
177
207
  }
178
208
  </script>
@@ -1,20 +1,22 @@
1
1
  <template>
2
- <div
3
- v-if="items.length"
4
- class="min-w-40 rounded-lg border bg-white p-1 text-base shadow-lg"
5
- >
6
- <button
7
- :class="[
8
- index === selectedIndex ? 'bg-gray-100' : 'text-gray-900',
9
- 'flex w-full items-center whitespace-nowrap rounded-md px-2 py-2 text-sm',
10
- ]"
11
- v-for="(item, index) in items"
12
- :key="index"
13
- @click="selectItem(index)"
14
- @mouseover="selectedIndex = index"
2
+ <div>
3
+ <div
4
+ v-if="items.length"
5
+ class="min-w-40 rounded-lg border bg-white p-1 text-base shadow-lg"
15
6
  >
16
- {{ item.label }}
17
- </button>
7
+ <button
8
+ :class="[
9
+ index === selectedIndex ? 'bg-gray-100' : 'text-gray-900',
10
+ 'flex w-full items-center whitespace-nowrap rounded-md px-2 py-2 text-sm',
11
+ ]"
12
+ v-for="(item, index) in items"
13
+ :key="index"
14
+ @click="selectItem(index)"
15
+ @mouseover="selectedIndex = index"
16
+ >
17
+ {{ item.label }}
18
+ </button>
19
+ </div>
18
20
  </div>
19
21
  </template>
20
22
 
@@ -88,7 +88,8 @@ import { Popover, Dialog, Input, Button } from 'pimelon-ui'
88
88
  import InsertImage from './InsertImage.vue'
89
89
  export default {
90
90
  name: 'TipTapMenu',
91
- props: ['editor', 'buttons'],
91
+ props: ['buttons'],
92
+ inject: ['editor'],
92
93
  components: {
93
94
  Popover,
94
95
  Dialog,