frappe-ui 0.1.216 → 0.1.220
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/frappe/Billing/SignupBanner.vue +1 -1
- package/frappe/Billing/TrialBanner.vue +1 -1
- package/frappe/Help/HelpModal.vue +4 -4
- package/frappe/Onboarding/GettingStartedBanner.vue +1 -1
- package/frappe/index.d.ts +53 -0
- package/icons/index.ts +10 -0
- package/package.json +27 -3
- package/src/components/Charts/index.ts +0 -0
- package/src/components/Combobox/Combobox.story.vue +18 -0
- package/src/components/Combobox/Combobox.vue +13 -2
- package/src/components/Combobox/types.ts +2 -1
- package/src/components/DatePicker/index.ts +6 -0
- package/src/components/Dropdown/Dropdown.vue +19 -5
- package/src/components/Dropdown/types.ts +1 -0
- package/src/components/ListView/ListGroupHeader.vue +1 -1
- package/src/components/ListView/ListGroupRows.vue +5 -0
- package/src/components/Select/Select.story.vue +16 -3
- package/src/components/Select/Select.vue +109 -96
- package/src/components/Sidebar/index.ts +3 -0
- package/src/components/Toast/Toast.vue +1 -1
- package/src/data-fetching/index.ts +4 -2
- package/src/index.ts +9 -23
- package/src/resources/{index.js → index.ts} +3 -1
- package/src/resources/{local.js → local.ts} +4 -4
- package/src/resources/realtime.ts +21 -0
- package/src/resources/realtime.js +0 -15
- /package/{src/icons/CircleCheck.vue → icons/CircleCheckIcon.vue} +0 -0
- /package/{src/icons/DownSolid.vue → icons/DownSolidIcon.vue} +0 -0
- /package/{src/components → icons}/GreenCheckIcon.vue +0 -0
- /package/{frappe/Icons → icons}/HelpIcon.vue +0 -0
- /package/{frappe/Icons → icons}/LightningIcon.vue +0 -0
- /package/{frappe/Icons → icons}/MaximizeIcon.vue +0 -0
- /package/{frappe/Icons → icons}/MinimizeIcon.vue +0 -0
- /package/{frappe/Icons → icons}/StepsIcon.vue +0 -0
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
</Button>
|
|
24
24
|
</template>
|
|
25
25
|
<script setup>
|
|
26
|
-
import LightningIcon from '
|
|
26
|
+
import LightningIcon from '../../icons/LightningIcon.vue'
|
|
27
27
|
import FeatherIcon from '../../src/components/FeatherIcon.vue'
|
|
28
28
|
import { Button } from '../../src/components/Button'
|
|
29
29
|
import { createResource } from '../../src/resources'
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
<script setup>
|
|
56
56
|
import Dropdown from '../../src/components/Dropdown/Dropdown.vue'
|
|
57
57
|
import Button from '../../src/components/Button/Button.vue'
|
|
58
|
-
import StepsIcon from '
|
|
59
|
-
import MinimizeIcon from '
|
|
60
|
-
import MaximizeIcon from '
|
|
61
|
-
import HelpIcon from '
|
|
58
|
+
import StepsIcon from '../../icons/StepsIcon.vue'
|
|
59
|
+
import MinimizeIcon from '../../icons/MinimizeIcon.vue'
|
|
60
|
+
import MaximizeIcon from '../../icons/MaximizeIcon.vue'
|
|
61
|
+
import HelpIcon from '../../icons/HelpIcon.vue'
|
|
62
62
|
import OnboardingSteps from '../Onboarding/OnboardingSteps.vue'
|
|
63
63
|
import HelpCenter from '../HelpCenter/HelpCenter.vue'
|
|
64
64
|
import { useOnboarding } from '../Onboarding/onboarding'
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
</Button>
|
|
57
57
|
</template>
|
|
58
58
|
<script setup>
|
|
59
|
-
import StepsIcon from '
|
|
59
|
+
import StepsIcon from '../../icons/StepsIcon.vue'
|
|
60
60
|
import Button from '../../src/components/Button/Button.vue'
|
|
61
61
|
import FeatherIcon from '../../src/components/FeatherIcon.vue'
|
|
62
62
|
import { useOnboarding } from './onboarding'
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// Since the export is via JS file, we need to declare the module here
|
|
2
|
+
declare module 'frappe-ui/frappe' {
|
|
3
|
+
import type { Component, ComputedRef, Ref } from 'vue'
|
|
4
|
+
|
|
5
|
+
// Onboarding
|
|
6
|
+
export interface OnboardingStep {
|
|
7
|
+
name: string
|
|
8
|
+
completed: boolean
|
|
9
|
+
[key: string]: any
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function useOnboarding(appName: string):
|
|
13
|
+
| {
|
|
14
|
+
steps: OnboardingStep[]
|
|
15
|
+
stepsCompleted: ComputedRef<number>
|
|
16
|
+
totalSteps: ComputedRef<number>
|
|
17
|
+
completedPercentage: ComputedRef<number>
|
|
18
|
+
isOnboardingStepsCompleted: Ref<boolean>
|
|
19
|
+
updateOnboardingStep: (
|
|
20
|
+
step: string,
|
|
21
|
+
value?: boolean,
|
|
22
|
+
skipped?: boolean,
|
|
23
|
+
callback?: ((step: string, skipped: boolean) => void) | null,
|
|
24
|
+
) => void
|
|
25
|
+
skip: (
|
|
26
|
+
step: string,
|
|
27
|
+
callback?: ((step: string, skipped: boolean) => void) | null,
|
|
28
|
+
) => void
|
|
29
|
+
skipAll: (callback?: ((value: boolean) => void) | null) => void
|
|
30
|
+
reset: (
|
|
31
|
+
step: string,
|
|
32
|
+
callback?: ((step: string, skipped: boolean) => void) | null,
|
|
33
|
+
) => void
|
|
34
|
+
resetAll: (callback?: ((value: boolean) => void) | null) => void
|
|
35
|
+
setUp: (steps: OnboardingStep[]) => void
|
|
36
|
+
syncStatus: () => void
|
|
37
|
+
}
|
|
38
|
+
| undefined
|
|
39
|
+
|
|
40
|
+
// Help Modal
|
|
41
|
+
export const showHelpModal: Ref<boolean>
|
|
42
|
+
export const minimize: Ref<boolean>
|
|
43
|
+
export const HelpModal: Component
|
|
44
|
+
|
|
45
|
+
// Banners
|
|
46
|
+
export const GettingStartedBanner: Component
|
|
47
|
+
export const TrialBanner: Component
|
|
48
|
+
export const IntermediateStepModal: Component
|
|
49
|
+
|
|
50
|
+
// Components
|
|
51
|
+
export const Link: Component
|
|
52
|
+
export type { LinkProps } from './Link/types'
|
|
53
|
+
}
|
package/icons/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { default as CircleCheckIcon } from './CircleCheckIcon.vue'
|
|
2
|
+
export { default as DownSolidIcon } from './DownSolidIcon.vue'
|
|
3
|
+
export { default as GreenCheckIcon } from './GreenCheckIcon.vue'
|
|
4
|
+
|
|
5
|
+
// Frappe Icons
|
|
6
|
+
export { default as HelpIcon } from './HelpIcon.vue'
|
|
7
|
+
export { default as LightningIcon } from './LightningIcon.vue'
|
|
8
|
+
export { default as MaximizeIcon } from './MaximizeIcon.vue'
|
|
9
|
+
export { default as MinimizeIcon } from './MinimizeIcon.vue'
|
|
10
|
+
export { default as StepsIcon } from './StepsIcon.vue'
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "frappe-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.220",
|
|
4
4
|
"description": "A set of components and utilities for rapid UI development",
|
|
5
|
-
"main": "./src/index.ts",
|
|
6
5
|
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
7
|
"scripts": {
|
|
8
8
|
"test": "vitest --run",
|
|
9
9
|
"type-check": "tsc --noEmit",
|
|
@@ -17,11 +17,35 @@
|
|
|
17
17
|
"story:build": "histoire build && cp 404.html .histoire/dist",
|
|
18
18
|
"story:preview": "histoire preview"
|
|
19
19
|
},
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"import": "./src/index.ts",
|
|
23
|
+
"types": "./src/index.ts"
|
|
24
|
+
},
|
|
25
|
+
"./frappe": {
|
|
26
|
+
"import": "./frappe/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./icons": {
|
|
29
|
+
"import": "./icons/index.ts"
|
|
30
|
+
},
|
|
31
|
+
"./tailwind": {
|
|
32
|
+
"import": "./src/tailwind/preset.js",
|
|
33
|
+
"default": "./src/tailwind/preset.js"
|
|
34
|
+
},
|
|
35
|
+
"./vite": {
|
|
36
|
+
"import": "./vite/index.js"
|
|
37
|
+
},
|
|
38
|
+
"./style.css": {
|
|
39
|
+
"import": "./src/style.css"
|
|
40
|
+
},
|
|
41
|
+
"./tsconfig.base.json": "./tsconfig.base.json"
|
|
42
|
+
},
|
|
20
43
|
"files": [
|
|
21
44
|
"frappe",
|
|
22
45
|
"src",
|
|
23
46
|
"scripts",
|
|
24
|
-
"vite"
|
|
47
|
+
"vite",
|
|
48
|
+
"icons"
|
|
25
49
|
],
|
|
26
50
|
"repository": {
|
|
27
51
|
"type": "git",
|
|
File without changes
|
|
@@ -178,6 +178,24 @@ const state = reactive({
|
|
|
178
178
|
</div>
|
|
179
179
|
</Variant>
|
|
180
180
|
|
|
181
|
+
<Variant title="Outline variant">
|
|
182
|
+
<div class="p-4">
|
|
183
|
+
<label class="block text-sm font-medium mb-2">Simple Options</label>
|
|
184
|
+
<Combobox
|
|
185
|
+
variant="outline"
|
|
186
|
+
:options="simpleOptions"
|
|
187
|
+
v-model="simpleValue"
|
|
188
|
+
:placeholder="state.placeholder"
|
|
189
|
+
:disabled="state.disabled"
|
|
190
|
+
:show-cancel="state.showCancel"
|
|
191
|
+
@update:selectedOption="selectedOption = $event"
|
|
192
|
+
/>
|
|
193
|
+
<div class="mt-2 text-sm text-gray-600">
|
|
194
|
+
Selected: {{ simpleValue || 'None' }}
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</Variant>
|
|
198
|
+
|
|
181
199
|
<Variant title="Object Options">
|
|
182
200
|
<div class="p-4">
|
|
183
201
|
<label class="block text-sm font-medium mb-2">Object Options</label>
|
|
@@ -33,6 +33,7 @@ import type {
|
|
|
33
33
|
} from './types'
|
|
34
34
|
|
|
35
35
|
const props = withDefaults(defineProps<ComboboxProps>(), {
|
|
36
|
+
variant: 'subtle',
|
|
36
37
|
options: () => [],
|
|
37
38
|
})
|
|
38
39
|
const emit = defineEmits([
|
|
@@ -279,6 +280,13 @@ const reset = () => {
|
|
|
279
280
|
emit('update:selectedOption', null)
|
|
280
281
|
}
|
|
281
282
|
|
|
283
|
+
const variantClasses = computed(() => {
|
|
284
|
+
return {
|
|
285
|
+
subtle: 'bg-surface-gray-2 hover:bg-surface-gray-3 border-transparent',
|
|
286
|
+
outline: 'border-outline-gray-2',
|
|
287
|
+
}[props.variant]
|
|
288
|
+
})
|
|
289
|
+
|
|
282
290
|
defineExpose({
|
|
283
291
|
reset,
|
|
284
292
|
})
|
|
@@ -294,8 +302,11 @@ defineExpose({
|
|
|
294
302
|
:open="isOpen"
|
|
295
303
|
>
|
|
296
304
|
<ComboboxAnchor
|
|
297
|
-
class="flex h-7 w-full items-center justify-between gap-2 rounded
|
|
298
|
-
:class="{
|
|
305
|
+
class="flex h-7 w-full items-center justify-between gap-2 rounded px-2 py-1 transition-colors border focus-within:border-outline-gray-4 focus-within:ring-2 focus-within:ring-outline-gray-3"
|
|
306
|
+
:class="{
|
|
307
|
+
'opacity-50 pointer-events-none': disabled,
|
|
308
|
+
[variantClasses]: true,
|
|
309
|
+
}"
|
|
299
310
|
@click="handleClick"
|
|
300
311
|
>
|
|
301
312
|
<div class="flex items-center gap-2 flex-1 overflow-hidden">
|
|
@@ -26,6 +26,7 @@ export type GroupedOption = { group: string; options: SimpleOption[] }
|
|
|
26
26
|
export type ComboboxOption = SimpleOption | GroupedOption
|
|
27
27
|
|
|
28
28
|
export interface ComboboxProps {
|
|
29
|
+
variant?: 'subtle' | 'outline'
|
|
29
30
|
options: Array<ComboboxOption>
|
|
30
31
|
modelValue?: string | null
|
|
31
32
|
placeholder?: string
|
|
@@ -33,4 +34,4 @@ export interface ComboboxProps {
|
|
|
33
34
|
openOnFocus?: boolean
|
|
34
35
|
openOnClick?: boolean
|
|
35
36
|
placement?: 'start' | 'center' | 'end'
|
|
36
|
-
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as DatePicker } from './DatePicker.vue'
|
|
2
|
+
export { default as DateRangePicker } from './DateRangePicker.vue'
|
|
3
|
+
export { default as DateTimePicker } from './DateTimePicker.vue'
|
|
4
|
+
export * from './types'
|
|
5
|
+
export { useDatePicker } from './useDatePicker'
|
|
6
|
+
export * from './utils'
|
|
@@ -53,7 +53,13 @@
|
|
|
53
53
|
:model-value="item.switchValue || false"
|
|
54
54
|
/>
|
|
55
55
|
</div>
|
|
56
|
-
<DropdownMenuItem
|
|
56
|
+
<DropdownMenuItem
|
|
57
|
+
v-else
|
|
58
|
+
as-child
|
|
59
|
+
@select="item.onClick"
|
|
60
|
+
:disabled="item.disabled"
|
|
61
|
+
class="data-[disabled]:cursor-not-allowed"
|
|
62
|
+
>
|
|
57
63
|
<slot v-if="$slots.item" name="item" v-bind="{ item, close }" />
|
|
58
64
|
<component
|
|
59
65
|
v-else-if="item.component"
|
|
@@ -160,6 +166,8 @@
|
|
|
160
166
|
(event: PointerEvent) =>
|
|
161
167
|
handleItemClick(subItem, event)
|
|
162
168
|
"
|
|
169
|
+
:disabled="subItem.disabled"
|
|
170
|
+
class="data-[disabled]:cursor-not-allowed"
|
|
163
171
|
>
|
|
164
172
|
<component
|
|
165
173
|
v-if="subItem.component"
|
|
@@ -310,10 +318,16 @@ const normalizeDropdownItem = (option: DropdownOption) => {
|
|
|
310
318
|
}
|
|
311
319
|
}
|
|
312
320
|
|
|
313
|
-
const getIconColor = (item: DropdownItem) =>
|
|
314
|
-
item.
|
|
315
|
-
|
|
316
|
-
|
|
321
|
+
const getIconColor = (item: DropdownItem) => {
|
|
322
|
+
if (item.disabled) return 'text-ink-gray-4'
|
|
323
|
+
return item.theme === 'red' ? 'text-ink-red-3' : 'text-ink-gray-6'
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const getTextColor = (item: DropdownItem) => {
|
|
327
|
+
if (item.disabled) return 'text-ink-gray-4'
|
|
328
|
+
return item.theme === 'red' ? 'text-ink-red-3' : 'text-ink-gray-7'
|
|
329
|
+
}
|
|
330
|
+
|
|
317
331
|
const getBackgroundColor = (item: DropdownItem) =>
|
|
318
332
|
item.theme === 'red'
|
|
319
333
|
? 'focus:bg-surface-red-3 data-[highlighted]:bg-surface-red-3 data-[state=open]:bg-surface-red-3'
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
<div class="mb-5 mt-2" v-if="!group.collapsed">
|
|
3
3
|
<slot>
|
|
4
4
|
<ListRow v-for="row in group.rows" :key="row[list.rowKey]" :row="row" />
|
|
5
|
+
<component
|
|
6
|
+
v-if="list.slots['group-empty'] && group.rows.length == 0"
|
|
7
|
+
:is="list.slots['group-empty']"
|
|
8
|
+
v-bind="{ group }"
|
|
9
|
+
/>
|
|
5
10
|
</slot>
|
|
6
11
|
</div>
|
|
7
12
|
</template>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref } from 'vue'
|
|
3
3
|
import Select from './Select.vue'
|
|
4
|
+
import LucideUser from '~icons/lucide/user'
|
|
4
5
|
|
|
5
6
|
const value = ref('')
|
|
6
7
|
const options = [
|
|
@@ -14,8 +15,20 @@ const options = [
|
|
|
14
15
|
</script>
|
|
15
16
|
<template>
|
|
16
17
|
<Story :layout="{ width: 500, type: 'grid' }">
|
|
17
|
-
<
|
|
18
|
-
<
|
|
19
|
-
|
|
18
|
+
<Variant title="Default">
|
|
19
|
+
<div class="p-2">
|
|
20
|
+
<Select :options="options" v-model="value" />
|
|
21
|
+
</div>
|
|
22
|
+
</Variant>
|
|
23
|
+
|
|
24
|
+
<Variant title="With prefix">
|
|
25
|
+
<div class="p-2">
|
|
26
|
+
<Select :options="options" v-model="value">
|
|
27
|
+
<template #prefix>
|
|
28
|
+
<LucideUser class="size-4 text-ink-gray-9" />
|
|
29
|
+
</template>
|
|
30
|
+
</Select>
|
|
31
|
+
</div>
|
|
32
|
+
</Variant>
|
|
20
33
|
</Story>
|
|
21
34
|
</template>
|
|
@@ -1,83 +1,25 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="relative flex items-center">
|
|
3
|
-
<div
|
|
4
|
-
:class="[
|
|
5
|
-
'absolute inset-y-0 left-0 flex items-center',
|
|
6
|
-
textColor,
|
|
7
|
-
prefixClasses,
|
|
8
|
-
]"
|
|
9
|
-
v-if="$slots.prefix"
|
|
10
|
-
>
|
|
11
|
-
<slot name="prefix"> </slot>
|
|
12
|
-
</div>
|
|
13
|
-
<div
|
|
14
|
-
v-if="placeholder"
|
|
15
|
-
v-show="!modelValue"
|
|
16
|
-
class="pointer-events-none absolute text-ink-gray-4 truncate w-full"
|
|
17
|
-
:class="[fontSizeClasses, paddingClasses]"
|
|
18
|
-
>
|
|
19
|
-
{{ placeholder }}
|
|
20
|
-
</div>
|
|
21
|
-
<select
|
|
22
|
-
:class="selectClasses"
|
|
23
|
-
:disabled="disabled"
|
|
24
|
-
:id="id"
|
|
25
|
-
:value="modelValue"
|
|
26
|
-
@change="handleChange"
|
|
27
|
-
v-bind="attrs"
|
|
28
|
-
>
|
|
29
|
-
<option
|
|
30
|
-
v-for="option in selectOptions"
|
|
31
|
-
:key="option.value"
|
|
32
|
-
:value="option.value"
|
|
33
|
-
:disabled="option.disabled || false"
|
|
34
|
-
:selected="modelValue === option.value"
|
|
35
|
-
>
|
|
36
|
-
{{ option.label }}
|
|
37
|
-
</option>
|
|
38
|
-
</select>
|
|
39
|
-
</div>
|
|
40
|
-
</template>
|
|
41
|
-
|
|
42
1
|
<script setup lang="ts">
|
|
43
|
-
import { computed
|
|
2
|
+
import { computed } from 'vue'
|
|
44
3
|
import type { SelectProps } from './types'
|
|
4
|
+
import LucideChevronDown from '~icons/lucide/chevron-down'
|
|
45
5
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
6
|
+
import {
|
|
7
|
+
SelectContent,
|
|
8
|
+
SelectItem,
|
|
9
|
+
SelectItemText,
|
|
10
|
+
SelectPortal,
|
|
11
|
+
SelectRoot,
|
|
12
|
+
SelectTrigger,
|
|
13
|
+
SelectValue,
|
|
14
|
+
SelectViewport,
|
|
15
|
+
} from 'reka-ui'
|
|
16
|
+
|
|
17
|
+
const model = defineModel<String>()
|
|
49
18
|
|
|
50
19
|
const props = withDefaults(defineProps<SelectProps>(), {
|
|
51
20
|
size: 'sm',
|
|
52
21
|
variant: 'subtle',
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const emit = defineEmits(['update:modelValue'])
|
|
56
|
-
const slots = useSlots()
|
|
57
|
-
const attrs = useAttrs()
|
|
58
|
-
|
|
59
|
-
function handleChange(e: Event) {
|
|
60
|
-
emit('update:modelValue', (e.target as HTMLInputElement).value)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const selectOptions = computed(() => {
|
|
64
|
-
return (
|
|
65
|
-
props.options
|
|
66
|
-
?.map((option) => {
|
|
67
|
-
if (typeof option === 'string') {
|
|
68
|
-
return {
|
|
69
|
-
label: option,
|
|
70
|
-
value: option,
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return option
|
|
74
|
-
})
|
|
75
|
-
.filter(Boolean) || []
|
|
76
|
-
)
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
const textColor = computed(() => {
|
|
80
|
-
return props.disabled ? 'text-ink-gray-5' : 'text-ink-gray-8'
|
|
22
|
+
placeholder: 'Select option',
|
|
81
23
|
})
|
|
82
24
|
|
|
83
25
|
const fontSizeClasses = computed(() => {
|
|
@@ -91,29 +33,29 @@ const fontSizeClasses = computed(() => {
|
|
|
91
33
|
|
|
92
34
|
const paddingClasses = computed(() => {
|
|
93
35
|
return {
|
|
94
|
-
sm: '
|
|
95
|
-
md: '
|
|
96
|
-
lg: '
|
|
97
|
-
xl: '
|
|
36
|
+
sm: 'px-2',
|
|
37
|
+
md: 'px-2.5 ',
|
|
38
|
+
lg: 'px-3',
|
|
39
|
+
xl: 'px-3',
|
|
98
40
|
}[props.size]
|
|
99
41
|
})
|
|
100
42
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}[props.size]
|
|
43
|
+
let sizeClasses = {
|
|
44
|
+
sm: 'rounded min-h-7',
|
|
45
|
+
md: 'rounded min-h-8',
|
|
46
|
+
lg: 'rounded-md min-h-10',
|
|
47
|
+
xl: 'rounded-md min-h-10',
|
|
48
|
+
}[props.size]
|
|
108
49
|
|
|
50
|
+
const selectClasses = computed(() => {
|
|
109
51
|
let variant = props.disabled ? 'disabled' : props.variant
|
|
110
52
|
let variantClasses = {
|
|
111
53
|
subtle:
|
|
112
|
-
'border border-[--surface-gray-2] bg-surface-gray-2 hover:border-outline-gray-modals hover:bg-surface-gray-3
|
|
54
|
+
'border border-[--surface-gray-2] bg-surface-gray-2 hover:border-outline-gray-modals hover:bg-surface-gray-3',
|
|
113
55
|
outline:
|
|
114
|
-
'border border-outline-gray-2 bg-surface-white hover:border-outline-gray-3
|
|
56
|
+
'border border-outline-gray-2 bg-surface-white hover:border-outline-gray-3',
|
|
115
57
|
ghost:
|
|
116
|
-
'bg-transparent border-transparent hover:bg-surface-gray-3 focus:bg-surface-gray-3
|
|
58
|
+
'bg-transparent border-transparent hover:bg-surface-gray-3 focus:bg-surface-gray-3',
|
|
117
59
|
disabled: [
|
|
118
60
|
'border',
|
|
119
61
|
props.variant !== 'ghost' ? 'bg-surface-gray-1' : '',
|
|
@@ -128,17 +70,88 @@ const selectClasses = computed(() => {
|
|
|
128
70
|
fontSizeClasses.value,
|
|
129
71
|
paddingClasses.value,
|
|
130
72
|
variantClasses,
|
|
131
|
-
|
|
132
|
-
'transition-colors w-full py-0 truncate',
|
|
73
|
+
'transition-colors w-full data-[state=open]:ring-2 ring-outline-gray-2 ',
|
|
133
74
|
]
|
|
134
75
|
})
|
|
135
76
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
lg: 'pl-3',
|
|
141
|
-
xl: 'pl-3',
|
|
142
|
-
}[props.size]
|
|
77
|
+
const selectOptions = computed(() => {
|
|
78
|
+
const str = typeof props.options?.[0] == 'string'
|
|
79
|
+
const tmp = props.options?.map((x) => ({ label: x, value: x }))
|
|
80
|
+
return (str ? tmp : props.options)?.filter(Boolean) || []
|
|
143
81
|
})
|
|
144
82
|
</script>
|
|
83
|
+
|
|
84
|
+
<template>
|
|
85
|
+
<SelectRoot v-model="model">
|
|
86
|
+
<SelectTrigger
|
|
87
|
+
class="inline-flex items-center gap-2 outline-none text-base data-[placeholder]:text-ink-gray-4 data-[disabled]:text-ink-gray-4"
|
|
88
|
+
aria-label="Customise options"
|
|
89
|
+
:class="selectClasses"
|
|
90
|
+
:disabled="props.disabled"
|
|
91
|
+
>
|
|
92
|
+
<slot name="prefix" />
|
|
93
|
+
<SelectValue :placeholder="props.placeholder" />
|
|
94
|
+
<LucideChevronDown class="size-4 text-ink-gray-4 ml-auto" />
|
|
95
|
+
</SelectTrigger>
|
|
96
|
+
|
|
97
|
+
<SelectPortal>
|
|
98
|
+
<SelectContent
|
|
99
|
+
class="bg-surface-modal border rounded-lg shadow-lg will-change-[opacity,transform] z-[100] min-w-[--reka-select-trigger-width] max-h-[--reka-select-content-available-height] overflow-auto"
|
|
100
|
+
:side-offset="5"
|
|
101
|
+
position="popper"
|
|
102
|
+
>
|
|
103
|
+
<SelectViewport class="p-1 flex flex-col">
|
|
104
|
+
<SelectItem
|
|
105
|
+
v-for="(option, index) in selectOptions"
|
|
106
|
+
:disabled="option.disabled"
|
|
107
|
+
:key="index"
|
|
108
|
+
:value="option.value"
|
|
109
|
+
:class="[sizeClasses, paddingClasses, fontSizeClasses]"
|
|
110
|
+
class="text-base text-ink-gray-9 flex items-center relative data-[highlighted]:bg-surface-gray-2 border-0 [data-state=checked]:bg-surface-gray-2 data-[disabled]:text-ink-gray-4"
|
|
111
|
+
>
|
|
112
|
+
<SelectItemText>
|
|
113
|
+
{{ option.label }}
|
|
114
|
+
</SelectItemText>
|
|
115
|
+
</SelectItem>
|
|
116
|
+
</SelectViewport>
|
|
117
|
+
</SelectContent>
|
|
118
|
+
</SelectPortal>
|
|
119
|
+
</SelectRoot>
|
|
120
|
+
</template>
|
|
121
|
+
|
|
122
|
+
<style>
|
|
123
|
+
@keyframes slideDownFade {
|
|
124
|
+
from {
|
|
125
|
+
opacity: 0;
|
|
126
|
+
transform: translateY(-4px) scale(0.98);
|
|
127
|
+
}
|
|
128
|
+
to {
|
|
129
|
+
opacity: 1;
|
|
130
|
+
transform: translateY(0) scale(1);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@keyframes slideUpFade {
|
|
135
|
+
from {
|
|
136
|
+
opacity: 0;
|
|
137
|
+
transform: translateY(4px) scale(0.98);
|
|
138
|
+
}
|
|
139
|
+
to {
|
|
140
|
+
opacity: 1;
|
|
141
|
+
transform: translateY(0) scale(1);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
[data-side='top'] {
|
|
146
|
+
animation: slideDownFade 280ms;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
[data-side='bottom'] {
|
|
150
|
+
animation: slideUpFade 280ms;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
[data-highlighted],
|
|
154
|
+
[data-state='checked'] {
|
|
155
|
+
outline: none !important;
|
|
156
|
+
}
|
|
157
|
+
</style>
|
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
export { default as Sidebar } from './Sidebar.vue'
|
|
2
|
+
export { default as SidebarHeader } from './SidebarHeader.vue'
|
|
3
|
+
export { default as SidebarItem } from './SidebarItem.vue'
|
|
4
|
+
export { default as SidebarSection } from './SidebarSection.vue'
|
|
2
5
|
export type { SidebarProps } from './types'
|
|
@@ -56,7 +56,7 @@ import { ToastAction, ToastClose, ToastDescription, ToastRoot } from 'reka-ui'
|
|
|
56
56
|
import LucideInfo from '~icons/lucide/info'
|
|
57
57
|
import LucideAlertTriangle from '~icons/lucide/alert-triangle'
|
|
58
58
|
import LucideX from '~icons/lucide/x'
|
|
59
|
-
import CircleCheck from '
|
|
59
|
+
import CircleCheck from '../../../icons/CircleCheckIcon.vue'
|
|
60
60
|
import type { ToastProps } from './types'
|
|
61
61
|
|
|
62
62
|
const props = defineProps<ToastProps>()
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
export * from './useCall/types'
|
|
1
2
|
export { useCall } from './useCall/useCall'
|
|
2
|
-
export { useList } from './useList/useList'
|
|
3
3
|
export { useDoc } from './useDoc/useDoc'
|
|
4
4
|
export { useDoctype } from './useDoctype/useDoctype'
|
|
5
|
-
export { useNewDoc } from './useNewDoc/useNewDoc'
|
|
6
5
|
export { useFrappeFetch } from './useFrappeFetch'
|
|
6
|
+
export * from './useList/types'
|
|
7
|
+
export { useList } from './useList/useList'
|
|
8
|
+
export { useNewDoc } from './useNewDoc/useNewDoc'
|
package/src/index.ts
CHANGED
|
@@ -9,9 +9,7 @@ export * from './components/Button'
|
|
|
9
9
|
export { default as Card } from './components/Card.vue'
|
|
10
10
|
export * from './components/Combobox'
|
|
11
11
|
export * from './components/Checkbox'
|
|
12
|
-
export
|
|
13
|
-
export { default as DateTimePicker } from './components/DatePicker/DateTimePicker.vue'
|
|
14
|
-
export { default as DateRangePicker } from './components/DatePicker/DateRangePicker.vue'
|
|
12
|
+
export * from './components/DatePicker'
|
|
15
13
|
export * from './components/Dialog'
|
|
16
14
|
export { default as Dialogs } from './components/Dialogs.vue'
|
|
17
15
|
export * from './components/Divider'
|
|
@@ -21,7 +19,6 @@ export { default as FeatherIcon } from './components/FeatherIcon.vue'
|
|
|
21
19
|
export * from './components/FileUploader'
|
|
22
20
|
export * from './components/FormControl'
|
|
23
21
|
export { default as FormLabel } from './components/FormLabel.vue'
|
|
24
|
-
export { default as GreenCheckIcon } from './components/GreenCheckIcon.vue'
|
|
25
22
|
export { default as Input } from './components/Input.vue'
|
|
26
23
|
export { default as ListItem } from './components/ListItem.vue'
|
|
27
24
|
export { default as LoadingIndicator } from './components/LoadingIndicator.vue'
|
|
@@ -67,7 +64,9 @@ export * from './components/Calendar'
|
|
|
67
64
|
export * from './components/CircularProgressBar'
|
|
68
65
|
export * from './components/Tree'
|
|
69
66
|
export { default as FrappeUIProvider } from './components/Provider/FrappeUIProvider.vue'
|
|
70
|
-
export
|
|
67
|
+
export * from './components/Sidebar/index.ts'
|
|
68
|
+
export { default as ConfirmDialog } from './components/ConfirmDialog.vue'
|
|
69
|
+
|
|
71
70
|
|
|
72
71
|
// grid layout
|
|
73
72
|
export { default as GridLayout } from './components/VueGridLayout/Layout.vue'
|
|
@@ -92,33 +91,20 @@ export { usePageMeta } from './utils/pageMeta'
|
|
|
92
91
|
export { dayjsLocal, dayjs } from './utils/dayjs'
|
|
93
92
|
export * from './utils/useFileUpload'
|
|
94
93
|
export * from './utils/theme'
|
|
94
|
+
export * from './components/TextEditor/extensions/image'
|
|
95
95
|
|
|
96
96
|
// old data-fetching: resources
|
|
97
|
-
export
|
|
98
|
-
|
|
99
|
-
createDocumentResource,
|
|
100
|
-
createListResource,
|
|
101
|
-
getCachedResource,
|
|
102
|
-
getCachedDocumentResource,
|
|
103
|
-
getCachedListResource,
|
|
104
|
-
resourcesPlugin,
|
|
105
|
-
} from './resources/index.js'
|
|
97
|
+
export * from './resources/index.ts'
|
|
98
|
+
|
|
106
99
|
export { request } from './utils/request.js'
|
|
107
100
|
export { frappeRequest } from './utils/frappeRequest.js'
|
|
108
101
|
export { default as initSocket } from './utils/socketio.js'
|
|
109
102
|
export { setConfig, getConfig } from './utils/config'
|
|
110
103
|
|
|
111
104
|
// new data-fetching composables
|
|
112
|
-
export
|
|
113
|
-
useCall,
|
|
114
|
-
useList,
|
|
115
|
-
useDoc,
|
|
116
|
-
useNewDoc,
|
|
117
|
-
useDoctype,
|
|
118
|
-
useFrappeFetch,
|
|
119
|
-
} from './data-fetching'
|
|
105
|
+
export * from './data-fetching'
|
|
120
106
|
|
|
121
107
|
// plugin
|
|
108
|
+
export { confirmDialog } from './utils/confirmDialog.js'
|
|
122
109
|
export { default as pageMetaPlugin } from './utils/pageMeta.js'
|
|
123
110
|
export { default as FrappeUI } from './utils/plugin.js'
|
|
124
|
-
export { confirmDialog } from './utils/confirmDialog.js'
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
export { createResource, getCachedResource } from './resources'
|
|
2
1
|
export {
|
|
3
2
|
createDocumentResource,
|
|
4
3
|
getCachedDocumentResource,
|
|
5
4
|
} from './documentResource'
|
|
6
5
|
export { createListResource, getCachedListResource } from './listResource'
|
|
6
|
+
export * from './local'
|
|
7
7
|
export { default as resourcesPlugin } from './plugin'
|
|
8
|
+
export * from './realtime'
|
|
9
|
+
export { createResource, getCachedResource } from './resources'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { get, set
|
|
1
|
+
import { del, get, set } from 'idb-keyval'
|
|
2
2
|
|
|
3
|
-
export function saveLocal(key, data) {
|
|
3
|
+
export function saveLocal<T = any>(key: string, data: T): Promise<void | null> {
|
|
4
4
|
if (typeof indexedDB === 'undefined') {
|
|
5
5
|
return Promise.resolve(null)
|
|
6
6
|
}
|
|
@@ -8,7 +8,7 @@ export function saveLocal(key, data) {
|
|
|
8
8
|
return set(key, JSON.stringify(data))
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export function deleteLocal(key) {
|
|
11
|
+
export function deleteLocal(key: string): Promise<void | null> {
|
|
12
12
|
if (typeof indexedDB === 'undefined') {
|
|
13
13
|
return Promise.resolve(null)
|
|
14
14
|
}
|
|
@@ -16,7 +16,7 @@ export function deleteLocal(key) {
|
|
|
16
16
|
return del(key)
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export function getLocal(key) {
|
|
19
|
+
export function getLocal<T = any>(key: string): Promise<T | null> {
|
|
20
20
|
if (typeof indexedDB === 'undefined') {
|
|
21
21
|
return Promise.resolve(null)
|
|
22
22
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Socket } from 'socket.io-client'
|
|
2
|
+
|
|
3
|
+
export function onDocUpdate(
|
|
4
|
+
socket: Socket,
|
|
5
|
+
doctype: string,
|
|
6
|
+
callback: (name: string) => void,
|
|
7
|
+
): void {
|
|
8
|
+
subscribe(socket, doctype)
|
|
9
|
+
socket.on('list_update', (data) => {
|
|
10
|
+
if (data.doctype == doctype) {
|
|
11
|
+
callback(data.name)
|
|
12
|
+
}
|
|
13
|
+
})
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let subscribed: Record<string, boolean> = {}
|
|
17
|
+
function subscribe(socket: Socket, doctype: string): void {
|
|
18
|
+
if (subscribed[doctype]) return
|
|
19
|
+
socket.emit('doctype_subscribe', doctype)
|
|
20
|
+
subscribed[doctype] = true
|
|
21
|
+
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export function onDocUpdate(socket, doctype, callback) {
|
|
2
|
-
subscribe(socket, doctype)
|
|
3
|
-
socket.on('list_update', (data) => {
|
|
4
|
-
if (data.doctype == doctype) {
|
|
5
|
-
callback(data.name)
|
|
6
|
-
}
|
|
7
|
-
})
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
let subscribed = {}
|
|
11
|
-
function subscribe(socket, doctype) {
|
|
12
|
-
if (subscribed[doctype]) return
|
|
13
|
-
socket.emit('doctype_subscribe', doctype)
|
|
14
|
-
subscribed[doctype] = true
|
|
15
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|