@revojs/ui 0.2.12 → 0.2.13

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.
Files changed (49) hide show
  1. package/dist/components/button.d.ts +1 -0
  2. package/dist/components/card.d.ts +8 -0
  3. package/dist/components/collection.d.ts +3 -0
  4. package/dist/components/container.d.ts +2 -3
  5. package/dist/components/dialog.d.ts +3 -2
  6. package/dist/components/entry.d.ts +6 -0
  7. package/dist/components/entry.js +1 -0
  8. package/dist/components/field.d.ts +2 -0
  9. package/dist/components/input.d.ts +1 -0
  10. package/dist/components/notification.d.ts +9 -0
  11. package/dist/components/notification.js +1 -0
  12. package/dist/components/pagination.d.ts +8 -0
  13. package/dist/components/pagination.js +1 -0
  14. package/dist/components/popover.d.ts +9 -1
  15. package/dist/components/select.d.ts +8 -5
  16. package/dist/components/sheet.d.ts +3 -2
  17. package/dist/components/shell.d.ts +6 -0
  18. package/dist/components/shell.js +1 -0
  19. package/dist/components/textarea.d.ts +13 -0
  20. package/dist/components/textarea.js +1 -0
  21. package/dist/index.d.ts +5 -0
  22. package/dist/module/index.d.ts +8 -0
  23. package/dist/module/index.js +16 -0
  24. package/dist/style.css +8 -0
  25. package/dist/vue/components/breadcrumb.vue +6 -1
  26. package/dist/vue/components/button.vue +25 -3
  27. package/dist/vue/components/card.vue +22 -3
  28. package/dist/vue/components/collection.vue +22 -7
  29. package/dist/vue/components/container.vue +6 -2
  30. package/dist/vue/components/dialog.vue +52 -35
  31. package/dist/vue/components/entry.vue +52 -0
  32. package/dist/vue/components/field.vue +37 -7
  33. package/dist/vue/components/input.vue +22 -2
  34. package/dist/vue/components/navigate.vue +1 -0
  35. package/dist/vue/components/notification.vue +58 -0
  36. package/dist/vue/components/pagination.vue +24 -0
  37. package/dist/vue/components/popover.vue +39 -15
  38. package/dist/vue/components/select.vue +93 -15
  39. package/dist/vue/components/sheet.vue +55 -21
  40. package/dist/vue/components/shell.vue +38 -0
  41. package/dist/vue/components/textarea.vue +49 -0
  42. package/dist/vue/components/upload.vue +43 -24
  43. package/dist/vue/index.d.ts +5 -0
  44. package/dist/vue/index.js +6 -1
  45. package/dist/vue/utilities/use-control-attributes.d.ts +11 -0
  46. package/dist/vue/utilities/use-control-attributes.js +14 -0
  47. package/dist/vue/utilities/use-modal-dialog.d.ts +9 -0
  48. package/dist/vue/utilities/use-modal-dialog.js +48 -0
  49. package/package.json +1 -1
@@ -0,0 +1,38 @@
1
+ <template>
2
+ <header class="z-10 shrink-0 border-b border-border bg-background" :class="sticky ? 'sticky top-0' : ''">
3
+ <Stack direction="horizontal" align="center" gap="1" padding="1">
4
+ <slot name="prefix" />
5
+
6
+ <h1 v-if="label || $slots.heading" class="min-w-0 flex-1 truncate p-3 text-sm font-semibold">
7
+ <slot name="heading">{{ label }}</slot>
8
+ </h1>
9
+ <template v-else-if="headingLabel">
10
+ <h1 class="sr-only">{{ headingLabel }}</h1>
11
+
12
+ <span aria-hidden="true" class="min-w-0 flex-1" />
13
+ </template>
14
+ <span v-else aria-hidden="true" class="min-w-0 flex-1" />
15
+
16
+ <slot name="actions"><slot /></slot>
17
+ </Stack>
18
+
19
+ <Stack
20
+ v-if="$slots.navigation"
21
+ is="nav"
22
+ :aria-label="navigationLabel"
23
+ direction="horizontal"
24
+ gap="1"
25
+ padding="1"
26
+ class="overflow-x-auto border-t border-border [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
27
+ >
28
+ <slot name="navigation" />
29
+ </Stack>
30
+ </header>
31
+ </template>
32
+
33
+ <script setup lang="ts">
34
+ import type { ShellProps } from "../../components/shell";
35
+ import Stack from "./stack.vue";
36
+
37
+ const { label, headingLabel, sticky = true, navigationLabel = "Page navigation" } = defineProps<ShellProps>();
38
+ </script>
@@ -0,0 +1,49 @@
1
+ <template>
2
+ <textarea
3
+ ref="control"
4
+ class="block w-full min-w-0 rounded-ui outline-ring placeholder:opacity-50 focus-visible:outline-2 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-2 aria-invalid:ring-destructive"
5
+ :class="[texts[text], resizes[resize], variants[variant]]"
6
+ :value="modelValue"
7
+ :disabled="disabled"
8
+ :readonly="readonly"
9
+ :required="required"
10
+ :aria-invalid="invalid ?? ($attrs as HTMLAttributes)['aria-invalid']"
11
+ @input="$emit('update:modelValue', ($event.currentTarget as HTMLTextAreaElement).value)"
12
+ ></textarea>
13
+ </template>
14
+
15
+ <script setup lang="ts">
16
+ import { useTemplateRef, type HTMLAttributes } from "vue";
17
+ import type { TextareaProps, TextareaResize, TextareaText, TextareaVariant } from "../../components/textarea";
18
+
19
+ const {
20
+ modelValue = "",
21
+ text = "sm",
22
+ resize = "vertical",
23
+ variant = "default",
24
+ disabled = false,
25
+ readonly = false,
26
+ required = false,
27
+ invalid = undefined,
28
+ } = defineProps<TextareaProps>();
29
+
30
+ defineEmits<{ "update:modelValue": [value: string] }>();
31
+
32
+ const control = useTemplateRef("control");
33
+
34
+ defineExpose({ focus: () => control.value?.focus() });
35
+
36
+ const texts: Record<TextareaText, string> = { sm: "text-sm", md: "text-base" };
37
+
38
+ const resizes: Record<TextareaResize, string> = {
39
+ none: "resize-none",
40
+ vertical: "resize-y",
41
+ horizontal: "resize-x",
42
+ both: "resize",
43
+ };
44
+
45
+ const variants: Record<TextareaVariant, string> = {
46
+ default: "bg-secondary p-3 ring-1 ring-input ring-inset",
47
+ plain: "bg-transparent p-0",
48
+ };
49
+ </script>
@@ -13,30 +13,34 @@
13
13
  @dragleave="leave"
14
14
  @drop.prevent="drop"
15
15
  >
16
- <Stack v-if="modelValue" direction="horizontal" align="center" justify="between" gap="0">
17
- <span class="min-w-0 flex-1 p-3">
18
- <span class="block truncate px-1">{{
19
- "name" in modelValue ? modelValue.name : "File selected"
20
- }}</span>
21
- </span>
22
-
23
- <Button
24
- size="icon"
25
- variant="secondary"
26
- :text="text"
27
- :icon-prefix="CloseIcon"
28
- aria-label="Remove file"
29
- :disabled="disabled"
30
- @click="remove"
31
- />
32
- </Stack>
33
- <Stack v-else align="center" gap="3" padding="5" class="text-center">
34
- <Icon :is="UploadIcon" :size="24" />
35
-
36
- <span class="text-sm opacity-60"> Drag and drop a file here </span>
37
-
38
- <Button label="Browse files" variant="outline" :disabled="disabled" @click="input?.click()" />
39
- </Stack>
16
+ <slot v-if="modelValue" name="file" :file="modelValue" :remove="remove" :disabled="disabled">
17
+ <Stack direction="horizontal" align="center" justify="between" gap="0">
18
+ <span class="min-w-0 flex-1 p-3">
19
+ <span class="block truncate px-1">{{
20
+ "name" in modelValue ? modelValue.name : "File selected"
21
+ }}</span>
22
+ </span>
23
+
24
+ <Button
25
+ size="icon"
26
+ variant="secondary"
27
+ :text="text"
28
+ :icon-prefix="CloseIcon"
29
+ aria-label="Remove file"
30
+ :disabled="disabled"
31
+ @click="remove"
32
+ />
33
+ </Stack>
34
+ </slot>
35
+ <slot v-else name="empty" :browse="browse" :disabled="disabled" :dragging="isOver">
36
+ <Stack align="center" gap="3" padding="5" class="text-center">
37
+ <Icon :is="UploadIcon" :size="24" />
38
+
39
+ <span class="text-sm opacity-60"> Drag and drop a file here </span>
40
+
41
+ <Button label="Browse files" variant="outline" :disabled="disabled" @click="browse" />
42
+ </Stack>
43
+ </slot>
40
44
 
41
45
  <input
42
46
  ref="input"
@@ -84,6 +88,11 @@ const emit = defineEmits<{
84
88
  "update:modelValue": [value?: Blob | File];
85
89
  }>();
86
90
 
91
+ defineSlots<{
92
+ empty?: (props: { browse: () => void; disabled: boolean; dragging: boolean }) => unknown;
93
+ file?: (props: { file: Blob | File; remove: () => void; disabled: boolean }) => unknown;
94
+ }>();
95
+
87
96
  const input = useTemplateRef("input");
88
97
 
89
98
  const isOver = ref(false);
@@ -107,6 +116,12 @@ const texts: Record<UploadText, string> = {
107
116
  md: "text-base",
108
117
  };
109
118
 
119
+ function browse() {
120
+ if (!disabled) {
121
+ input.value?.click();
122
+ }
123
+ }
124
+
110
125
  function change() {
111
126
  const file = input.value?.files?.item(0);
112
127
 
@@ -137,6 +152,10 @@ function leave(event: DragEvent) {
137
152
  }
138
153
 
139
154
  function remove() {
155
+ if (disabled) {
156
+ return;
157
+ }
158
+
140
159
  if (input.value) {
141
160
  input.value.value = "";
142
161
  }
@@ -8,11 +8,15 @@ export { default as Counter } from "./components/counter.vue";
8
8
  export { default as Dialog } from "./components/dialog.vue";
9
9
  export { default as Field } from "./components/field.vue";
10
10
  export { default as Heading } from "./components/heading.vue";
11
+ export { default as Shell } from "./components/shell.vue";
11
12
  export { default as Icon } from "./components/icon.vue";
12
13
  export { default as Input } from "./components/input.vue";
13
14
  export { default as Link } from "./components/link.vue";
15
+ export { default as Entry } from "./components/entry.vue";
16
+ export { default as Notification } from "./components/notification.vue";
14
17
  export { default as Navigate } from "./components/navigate.vue";
15
18
  export { default as Popover } from "./components/popover.vue";
19
+ export { default as Pagination } from "./components/pagination.vue";
16
20
  export { default as Progress } from "./components/progress.vue";
17
21
  export { default as Radio } from "./components/radio.vue";
18
22
  export { default as Range } from "./components/range.vue";
@@ -23,5 +27,6 @@ export { default as Sheet } from "./components/sheet.vue";
23
27
  export { default as Stack } from "./components/stack.vue";
24
28
  export { default as Switch } from "./components/switch.vue";
25
29
  export { default as Upload } from "./components/upload.vue";
30
+ export { default as Textarea } from "./components/textarea.vue";
26
31
  export { useFocusTrap } from "./utilities/use-focus-trap";
27
32
  export { useScrollLock } from "./utilities/use-scroll-lock";
package/dist/vue/index.js CHANGED
@@ -10,11 +10,15 @@ import Counter from "./components/counter.vue";
10
10
  import Dialog from "./components/dialog.vue";
11
11
  import Field from "./components/field.vue";
12
12
  import Heading from "./components/heading.vue";
13
+ import Shell from "./components/shell.vue";
13
14
  import Icon from "./components/icon.vue";
14
15
  import Input from "./components/input.vue";
15
16
  import Link from "./components/link.vue";
17
+ import Entry from "./components/entry.vue";
18
+ import Notification from "./components/notification.vue";
16
19
  import Navigate from "./components/navigate.vue";
17
20
  import Popover from "./components/popover.vue";
21
+ import Pagination from "./components/pagination.vue";
18
22
  import Progress from "./components/progress.vue";
19
23
  import Radio from "./components/radio.vue";
20
24
  import Range from "./components/range.vue";
@@ -25,4 +29,5 @@ import Sheet from "./components/sheet.vue";
25
29
  import Stack from "./components/stack.vue";
26
30
  import Switch from "./components/switch.vue";
27
31
  import Upload from "./components/upload.vue";
28
- export { Breadcrumb, Button, Card, Checkbox, Collection, Container, Counter, Dialog, Field, Heading, Icon, Input, Link, Navigate, Popover, Progress, Radio, Range, Rating, Select, Separator, Sheet, Stack, Switch, Upload, useFocusTrap, useScrollLock };
32
+ import Textarea from "./components/textarea.vue";
33
+ export { Breadcrumb, Button, Card, Checkbox, Collection, Container, Counter, Dialog, Entry, Field, Heading, Icon, Input, Link, Navigate, Notification, Pagination, Popover, Progress, Radio, Range, Rating, Select, Separator, Sheet, Shell, Stack, Switch, Textarea, Upload, useFocusTrap, useScrollLock };
@@ -0,0 +1,11 @@
1
+ import { type InputHTMLAttributes } from "vue";
2
+ export declare function useControlAttributes(): {
3
+ rootAttributes: Readonly<import("vue").Ref<{
4
+ class: import("vue").ClassValue;
5
+ style: import("vue").StyleValue;
6
+ }, {
7
+ class: import("vue").ClassValue;
8
+ style: import("vue").StyleValue;
9
+ }>>;
10
+ controlAttributes: Readonly<import("vue").Ref<InputHTMLAttributes, InputHTMLAttributes>>;
11
+ };
@@ -0,0 +1,14 @@
1
+ import { toRef, useAttrs } from "vue";
2
+ //#region src/vue/utilities/use-control-attributes.ts
3
+ function useControlAttributes() {
4
+ const attributes = useAttrs();
5
+ return {
6
+ rootAttributes: toRef(() => ({
7
+ class: attributes.class,
8
+ style: attributes.style
9
+ })),
10
+ controlAttributes: toRef(() => Object.fromEntries(Object.entries(attributes).filter(([key]) => key !== "class" && key !== "style")))
11
+ };
12
+ }
13
+ //#endregion
14
+ export { useControlAttributes };
@@ -0,0 +1,9 @@
1
+ import { type MaybeRefOrGetter, type Ref } from "vue";
2
+ export declare function useModalDialog(element: Ref<HTMLDialogElement | null>, modelValue: MaybeRefOrGetter<boolean | undefined>, update: (value: boolean) => void, dismissible: MaybeRefOrGetter<boolean>): {
3
+ isOpen: import("vue").ComputedRef<boolean>;
4
+ open: () => void;
5
+ close: () => void;
6
+ toggle: () => void;
7
+ dismiss: (event: Event) => void;
8
+ handleClose: () => void;
9
+ };
@@ -0,0 +1,48 @@
1
+ import { useScrollLock } from "./use-scroll-lock.js";
2
+ import { computed, ref, toValue, watchEffect } from "vue";
3
+ //#region src/vue/utilities/use-modal-dialog.ts
4
+ function useModalDialog(element, modelValue, update, dismissible) {
5
+ function open() {
6
+ internalOpen.value = true;
7
+ update(true);
8
+ }
9
+ function close() {
10
+ internalOpen.value = false;
11
+ element.value?.close();
12
+ update(false);
13
+ }
14
+ function toggle() {
15
+ if (isOpen.value) close();
16
+ else open();
17
+ }
18
+ function dismiss(event) {
19
+ if (!toValue(dismissible)) {
20
+ event.preventDefault();
21
+ return;
22
+ }
23
+ close();
24
+ }
25
+ function handleClose() {
26
+ if (element.value?.open) return;
27
+ internalOpen.value = false;
28
+ if (isOpen.value) update(false);
29
+ }
30
+ const internalOpen = ref(false);
31
+ const isOpen = computed(() => toValue(modelValue) ?? internalOpen.value);
32
+ useScrollLock(isOpen);
33
+ watchEffect(() => {
34
+ if (!element.value?.isConnected) return;
35
+ if (isOpen.value && !element.value.open) element.value.showModal();
36
+ else if (!isOpen.value && element.value.open) element.value.close();
37
+ }, { flush: "post" });
38
+ return {
39
+ isOpen,
40
+ open,
41
+ close,
42
+ toggle,
43
+ dismiss,
44
+ handleClose
45
+ };
46
+ }
47
+ //#endregion
48
+ export { useModalDialog };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revojs/ui",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",