orio-ui 1.8.0 → 1.9.0

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/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.0.0 || ^4.0.0"
6
6
  },
7
- "version": "1.8.0",
7
+ "version": "1.9.0",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "0.8.4",
10
10
  "unbuild": "2.0.0"
@@ -0,0 +1,58 @@
1
+ <script setup lang="ts">
2
+ import { usePressAndHold } from "../../composables/usePressAndHold";
3
+
4
+ interface Props {
5
+ min?: number;
6
+ max?: number;
7
+ step?: number;
8
+ decimalPlaces?: number;
9
+ disabled?: boolean;
10
+ }
11
+
12
+ withDefaults(defineProps<Props>(), {
13
+ min: undefined,
14
+ max: undefined,
15
+ step: 1,
16
+ decimalPlaces: 0,
17
+ disabled: false,
18
+ });
19
+
20
+ const modelValue = defineModel<number>({ default: 0 });
21
+
22
+ const { pressAndHold, stop } = usePressAndHold();
23
+ </script>
24
+
25
+ <template>
26
+ <orio-number-input v-model="modelValue" v-bind="$props" class="horizontal">
27
+ <template #controls="{ increase, decrease, isAtMax, isAtMin }">
28
+ <orio-button
29
+ appearance="minimal"
30
+ icon="minus"
31
+ variant="subdued"
32
+ :disabled="isAtMin || disabled"
33
+ @mousedown="pressAndHold(decrease)"
34
+ @mouseup="stop"
35
+ @mouseleave="stop"
36
+ />
37
+ <orio-button
38
+ appearance="minimal"
39
+ icon="plus"
40
+ variant="subdued"
41
+ :disabled="isAtMax || disabled"
42
+ @mousedown="pressAndHold(increase)"
43
+ @mouseup="stop"
44
+ @mouseleave="stop"
45
+ />
46
+ </template>
47
+ </orio-number-input>
48
+ </template>
49
+
50
+ <style scoped>
51
+ .horizontal :deep(.number-input) {
52
+ text-align: center;
53
+ }
54
+ .horizontal :deep(.controls) {
55
+ justify-content: space-between;
56
+ padding: 0 3px;
57
+ }
58
+ </style>
@@ -0,0 +1,57 @@
1
+ <script setup lang="ts">
2
+ import { usePressAndHold } from "../../composables/usePressAndHold";
3
+
4
+ interface Props {
5
+ min?: number;
6
+ max?: number;
7
+ step?: number;
8
+ decimalPlaces?: number;
9
+ disabled?: boolean;
10
+ }
11
+
12
+ withDefaults(defineProps<Props>(), {
13
+ min: undefined,
14
+ max: undefined,
15
+ step: 1,
16
+ decimalPlaces: 0,
17
+ disabled: false,
18
+ });
19
+
20
+ const modelValue = defineModel<number>({ default: 0 });
21
+
22
+ const { pressAndHold, stop } = usePressAndHold();
23
+ </script>
24
+
25
+ <template>
26
+ <orio-number-input v-model="modelValue" v-bind="$props" class="vertical">
27
+ <template #controls="{ increase, decrease, isAtMax, isAtMin }">
28
+ <orio-button
29
+ appearance="minimal"
30
+ icon="chevron-up"
31
+ variant="subdued"
32
+ :disabled="isAtMax || disabled"
33
+ @mousedown="pressAndHold(increase)"
34
+ @mouseup="stop"
35
+ @mouseleave="stop"
36
+ />
37
+ <orio-button
38
+ appearance="minimal"
39
+ icon="chevron-down"
40
+ variant="subdued"
41
+ :disabled="isAtMin || disabled"
42
+ @mousedown="pressAndHold(decrease)"
43
+ @mouseup="stop"
44
+ @mouseleave="stop"
45
+ />
46
+ </template>
47
+ </orio-number-input>
48
+ </template>
49
+
50
+ <style scoped>
51
+ .vertical :deep(.controls) {
52
+ flex-direction: column;
53
+ justify-content: space-around;
54
+ right: 3px;
55
+ left: auto;
56
+ }
57
+ </style>
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { ref, toRefs } from "vue";
2
+ import { computed, toRefs } from "vue";
3
3
 
4
4
  interface Props {
5
5
  min?: number;
@@ -19,27 +19,6 @@ const { min, max, step, decimalPlaces } = toRefs(props);
19
19
 
20
20
  const modelValue = defineModel<number>({ default: 0 });
21
21
 
22
- const interval = ref<number | null>(null);
23
- const timeout = ref<number | null>(null);
24
-
25
- function pressAndHold(callback: () => void) {
26
- callback();
27
- timeout.value = window.setTimeout(() => {
28
- interval.value = window.setInterval(callback, 50);
29
- }, 500);
30
- }
31
-
32
- function stop() {
33
- if (timeout.value) {
34
- window.clearTimeout(timeout.value);
35
- timeout.value = null;
36
- }
37
- if (interval.value) {
38
- window.clearInterval(interval.value);
39
- interval.value = null;
40
- }
41
- }
42
-
43
22
  function setValidatedValue(value: number) {
44
23
  let finalValue = value;
45
24
 
@@ -55,6 +34,33 @@ function setValidatedValue(value: number) {
55
34
 
56
35
  modelValue.value = finalValue;
57
36
  }
37
+
38
+ function onBlur() {
39
+ setValidatedValue(modelValue.value);
40
+ }
41
+
42
+ function increase() {
43
+ setValidatedValue(modelValue.value + step.value);
44
+ }
45
+
46
+ function decrease() {
47
+ setValidatedValue(modelValue.value - step.value);
48
+ }
49
+
50
+ const isAtMax = computed(
51
+ () => Number.isFinite(max.value) && modelValue.value >= (max.value as number),
52
+ );
53
+
54
+ const isAtMin = computed(
55
+ () => Number.isFinite(min.value) && modelValue.value <= (min.value as number),
56
+ );
57
+
58
+ const slotExpose = computed(() => ({
59
+ increase,
60
+ decrease,
61
+ isAtMax: isAtMax.value,
62
+ isAtMin: isAtMin.value,
63
+ }));
58
64
  </script>
59
65
 
60
66
  <template>
@@ -65,30 +71,20 @@ function setValidatedValue(value: number) {
65
71
  v-model="modelValue"
66
72
  type="number"
67
73
  class="number-input"
74
+ @blur="onBlur"
68
75
  />
69
76
  <div class="controls">
70
- <orio-button
71
- appearance="minimal"
72
- icon="chevron-up"
73
- :disabled="Number.isFinite(max) && modelValue >= (max as number)"
74
- @mousedown="pressAndHold(() => setValidatedValue(modelValue + step))"
75
- @mouseup="stop"
76
- @mouseleave="stop"
77
- />
78
- <orio-button
79
- appearance="minimal"
80
- icon="chevron-down"
81
- :disabled="Number.isFinite(min) && modelValue <= (min as number)"
82
- @mousedown="pressAndHold(() => setValidatedValue(modelValue - step))"
83
- @mouseup="stop"
84
- @mouseleave="stop"
85
- />
77
+ <slot name="controls" v-bind="slotExpose" />
86
78
  </div>
87
79
  </div>
88
80
  </orio-control-element>
89
81
  </template>
90
82
 
91
83
  <style scoped>
84
+ .wrapper {
85
+ position: relative;
86
+ }
87
+
92
88
  .number-input {
93
89
  width: 100%;
94
90
  padding: 0.5rem 0.75rem;
@@ -117,20 +113,14 @@ function setValidatedValue(value: number) {
117
113
  cursor: not-allowed;
118
114
  }
119
115
 
120
- .wrapper {
121
- position: relative;
122
- }
123
-
124
116
  .controls {
125
117
  position: absolute;
126
- height: 100%;
127
- right: 3px;
128
- top: 0;
118
+ inset: 0;
129
119
  display: flex;
130
- flex-direction: column;
131
- justify-content: space-around;
120
+ align-items: center;
121
+ pointer-events: none;
132
122
  }
133
- .controls div {
134
- margin: 0.25rem;
123
+ .controls :deep(button) {
124
+ pointer-events: auto;
135
125
  }
136
126
  </style>
@@ -3,3 +3,4 @@ export { useFuzzySearch } from "./useFuzzySearch.js";
3
3
  export { useModal, type ModalProps, type OriginRect } from "./useModal.js";
4
4
  export { useTheme } from "./useTheme.js";
5
5
  export { useDecimalFormatter } from "./useDecimalFormatter.js";
6
+ export { usePressAndHold } from "./usePressAndHold.js";
@@ -5,3 +5,4 @@ export { useFuzzySearch } from "./useFuzzySearch.js";
5
5
  export { useModal } from "./useModal.js";
6
6
  export { useTheme } from "./useTheme.js";
7
7
  export { useDecimalFormatter } from "./useDecimalFormatter.js";
8
+ export { usePressAndHold } from "./usePressAndHold.js";
@@ -0,0 +1,4 @@
1
+ export declare function usePressAndHold(): {
2
+ pressAndHold: (callback: () => void) => void;
3
+ stop: () => void;
4
+ };
@@ -0,0 +1,22 @@
1
+ import { ref } from "vue";
2
+ export function usePressAndHold() {
3
+ const interval = ref(null);
4
+ const timeout = ref(null);
5
+ function pressAndHold(callback) {
6
+ callback();
7
+ timeout.value = window.setTimeout(() => {
8
+ interval.value = window.setInterval(callback, 50);
9
+ }, 500);
10
+ }
11
+ function stop() {
12
+ if (timeout.value) {
13
+ window.clearTimeout(timeout.value);
14
+ timeout.value = null;
15
+ }
16
+ if (interval.value) {
17
+ window.clearInterval(interval.value);
18
+ interval.value = null;
19
+ }
20
+ }
21
+ return { pressAndHold, stop };
22
+ }
@@ -1,6 +1,9 @@
1
1
  export { default as Button } from "./components/Button.vue.js";
2
2
  export { default as NavButton } from "./components/NavButton.vue.js";
3
3
  export { default as Input } from "./components/Input.vue.js";
4
+ export { default as NumberInput } from "./components/NumberInput/index.vue.js";
5
+ export { default as NumberInputVertical } from "./components/NumberInput/Vertical.vue.js";
6
+ export { default as NumberInputHorizontal } from "./components/NumberInput/Horizontal.vue.js";
4
7
  export { default as Textarea } from "./components/Textarea.vue.js";
5
8
  export { default as CheckBox } from "./components/CheckBox.vue.js";
6
9
  export { default as SwitchButton } from "./components/SwitchButton.vue.js";
@@ -13,7 +16,7 @@ export { default as LoadingSpinner } from "./components/LoadingSpinner.vue.js";
13
16
  export { default as Modal } from "./components/Modal.vue.js";
14
17
  export { default as Popover } from "./components/Popover.vue.js";
15
18
  export { default as Tooltip } from "./components/Tooltip.vue.js";
16
- export { default as Upload } from "./components/Upload.vue.js";
19
+ export { default as Upload } from "./components/Upload/index.vue.js";
17
20
  export { default as EmptyState } from "./components/EmptyState.vue.js";
18
21
  export { default as DashedContainer } from "./components/DashedContainer.vue.js";
19
22
  export { default as ControlElement } from "./components/ControlElement.vue.js";
@@ -1,6 +1,9 @@
1
1
  export { default as Button } from "./components/Button.vue";
2
2
  export { default as NavButton } from "./components/NavButton.vue";
3
3
  export { default as Input } from "./components/Input.vue";
4
+ export { default as NumberInput } from "./components/NumberInput/index.vue";
5
+ export { default as NumberInputVertical } from "./components/NumberInput/Vertical.vue";
6
+ export { default as NumberInputHorizontal } from "./components/NumberInput/Horizontal.vue";
4
7
  export { default as Textarea } from "./components/Textarea.vue";
5
8
  export { default as CheckBox } from "./components/CheckBox.vue";
6
9
  export { default as SwitchButton } from "./components/SwitchButton.vue";
@@ -13,7 +16,7 @@ export { default as LoadingSpinner } from "./components/LoadingSpinner.vue";
13
16
  export { default as Modal } from "./components/Modal.vue";
14
17
  export { default as Popover } from "./components/Popover.vue";
15
18
  export { default as Tooltip } from "./components/Tooltip.vue";
16
- export { default as Upload } from "./components/Upload.vue";
19
+ export { default as Upload } from "./components/Upload/index.vue";
17
20
  export { default as EmptyState } from "./components/EmptyState.vue";
18
21
  export { default as DashedContainer } from "./components/DashedContainer.vue";
19
22
  export { default as ControlElement } from "./components/ControlElement.vue";
@@ -32,6 +32,8 @@ export const iconRegistry = {
32
32
  check: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M9 16.17L4.83 12l-1.42 1.41L9 19L21 7l-1.41-1.41z"/></svg>`,
33
33
  // Plus / Add
34
34
  plus: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>`,
35
+ // Minus / Remove
36
+ minus: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M19 13H5v-2h14v2z"/></svg>`,
35
37
  // Calendar
36
38
  calendar: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M19 4h-1V2h-2v2H8V2H6v2H5c-1.11 0-1.99.9-1.99 2L3 20a2 2 0 0 0 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2m0 16H5V10h14zm0-12H5V6h14zM7 12h5v5H7z"/></svg>`,
37
39
  // Close / X
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orio-ui",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Modern Nuxt component library with theme support",
5
5
  "type": "module",
6
6
  "main": "./dist/module.mjs",