@espri/vue-components 1.1.0 → 2.0.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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @espri/vue-components
2
2
 
3
- ESPRI Digital Vue 3 component library built on top of PrimeVue.
3
+ ESPRI Digital Vue 3 component. Inputs components are built on top of PrimeVue.
4
4
 
5
5
  ---
6
6
 
@@ -13,10 +13,21 @@ npm install @espri/vue-components
13
13
  ## Import example
14
14
 
15
15
  ```javascript
16
+ // Option 1: Import from the main Vue components entry
16
17
  import { EspriInputText } from '@espri/vue-components';
18
+
19
+ // Option 2: Import the specific component directly
20
+ import EspriInputNumber from '@espri/vue-components/input-number';
17
21
  ```
18
22
 
19
23
  ## Available Components
20
24
 
25
+ ### Inputs
26
+
27
+ - `EspriInputNumber`
21
28
  - `EspriInputText`
22
29
  - `EspriSelect`
30
+
31
+ ### Others
32
+
33
+ - `EspriVirtualKeyboard`
package/dist/index.es.js CHANGED
@@ -1,6 +1,10 @@
1
- import { default as r } from "./input-text.es.js";
2
- import { default as a } from "./select.es.js";
1
+ import { default as t } from "./input-number.es.js";
2
+ import { default as p } from "./input-text.es.js";
3
+ import { default as f } from "./select.es.js";
4
+ import { default as u } from "./virtual-keyboard.es.js";
3
5
  export {
4
- r as EspriInputText,
5
- a as EspriSelect
6
+ t as EspriInputNumber,
7
+ p as EspriInputText,
8
+ f as EspriSelect,
9
+ u as EspriVirtualKeyboard
6
10
  };
@@ -0,0 +1,79 @@
1
+ import { defineComponent as b, computed as m, openBlock as o, createElementBlock as d, normalizeClass as u, createBlock as c, unref as a, withCtx as V, createVNode as _, createElementVNode as p, createTextVNode as h, toDisplayString as f, createCommentVNode as v } from "vue";
2
+ import { Skeleton as k, FloatLabel as B, InputNumber as N } from "primevue";
3
+ const D = ["for"], E = {
4
+ key: 0,
5
+ class: "text-danger"
6
+ }, F = { class: "text-danger" }, C = /* @__PURE__ */ b({
7
+ __name: "input-number",
8
+ props: {
9
+ label: {},
10
+ modelValue: {},
11
+ class: {},
12
+ classInput: {},
13
+ disabled: { type: Boolean },
14
+ required: { type: Boolean },
15
+ step: {},
16
+ minFractionDigits: {},
17
+ maxFractionDigits: {},
18
+ loading: { type: Boolean },
19
+ showError: { type: Boolean },
20
+ mode: {},
21
+ currency: {},
22
+ suffix: {},
23
+ min: {},
24
+ max: {},
25
+ rules: {},
26
+ placeholder: {}
27
+ },
28
+ emits: ["update:modelValue"],
29
+ setup(x, { emit: g }) {
30
+ const e = x, i = g, n = Math.random().toString(16).slice(2), s = m({
31
+ get() {
32
+ return (e.modelValue || e.modelValue === 0) && (typeof e.min == "number" && e.modelValue < e.min || typeof e.max == "number" && e.modelValue > e.max) ? (i("update:modelValue", null), null) : e.modelValue;
33
+ },
34
+ set(t) {
35
+ (t || t === 0) && (typeof e.min == "number" && t < e.min || typeof e.max == "number" && t > e.max) && (t = null), i("update:modelValue", t);
36
+ }
37
+ }), r = m(() => e.showError && e.rules?.find((l) => l?.$invalid && l?.$message)?.$message || "");
38
+ return (t, l) => (o(), d("div", {
39
+ class: u(`field ${e.class || ""}`)
40
+ }, [
41
+ e.loading ? (o(), c(a(k), {
42
+ key: 0,
43
+ class: "skeleton-input mt-3"
44
+ })) : (o(), c(a(B), {
45
+ key: 1,
46
+ class: "mt-3"
47
+ }, {
48
+ default: V(() => [
49
+ _(a(N), {
50
+ modelValue: s.value,
51
+ "onUpdate:modelValue": l[0] || (l[0] = (y) => s.value = y),
52
+ class: u(["w-full", e.classInput]),
53
+ min: e.min,
54
+ max: e.max,
55
+ mode: e.mode,
56
+ step: e.step,
57
+ "min-fraction-digits": e.minFractionDigits,
58
+ "max-fraction-digits": e.maxFractionDigits,
59
+ currency: e.currency,
60
+ suffix: e.suffix,
61
+ invalid: !!r.value,
62
+ disabled: e.disabled,
63
+ placeholder: e.placeholder,
64
+ "input-id": a(n)
65
+ }, null, 8, ["modelValue", "min", "max", "mode", "step", "min-fraction-digits", "max-fraction-digits", "currency", "suffix", "invalid", "class", "disabled", "placeholder", "input-id"]),
66
+ p("label", { for: a(n) }, [
67
+ h(f(e.label), 1),
68
+ e.required ? (o(), d("span", E, " *")) : v("", !0)
69
+ ], 8, D)
70
+ ]),
71
+ _: 1
72
+ })),
73
+ p("small", F, f(r.value), 1)
74
+ ], 2));
75
+ }
76
+ }), S = C;
77
+ export {
78
+ S as default
79
+ };
@@ -1,3 +1,5 @@
1
+ import EspriInputNumber from './input-number';
1
2
  import EspriInputText from './input-text';
2
3
  import EspriSelect from './select';
3
- export { EspriInputText, EspriSelect, };
4
+ import EspriVirtualKeyboard from './virtual-keyboard';
5
+ export { EspriInputNumber, EspriInputText, EspriSelect, EspriVirtualKeyboard, };
@@ -0,0 +1,19 @@
1
+ import type { DefineComponent } from 'vue';
2
+ import type EspriNumberTextEmits from './interfaces/input-number-emits.interface';
3
+ import type { EspriInputNumberEmitsForDoc } from './interfaces/input-number-emits.interface';
4
+ import type EspriNumberTextProps from './interfaces/input-number-props.interface';
5
+ /**
6
+ * **ESPRI Digital - Input Number (Vue component)**
7
+ * --- ---
8
+ * ![ESPRIDigital](https://espridigital.fr/wp-content/uploads/2024/09/logo_espri_digital_white-100-1.png)
9
+ *
10
+ */
11
+ declare const EspriNumberText: DefineComponent<EspriNumberTextProps, {}, // For slots if any
12
+ {}, // For methods or data if any
13
+ {}, // Computed
14
+ {}, // Methods
15
+ {}, // Mixin
16
+ {}, // Extends
17
+ EspriInputNumberEmitsForDoc>;
18
+ export default EspriNumberText;
19
+ export type { EspriNumberTextEmits, EspriNumberTextProps };
@@ -0,0 +1,10 @@
1
+ import type { ObjectEmitsOptions } from 'vue';
2
+ export default interface EspriNumberTextEmits {
3
+ (e: 'update:modelValue', value: any): void;
4
+ }
5
+ export interface EspriInputNumberEmitsForDoc extends ObjectEmitsOptions {
6
+ /**
7
+ * Called when modelValue is updated.
8
+ */
9
+ 'update:modelValue': (value: any) => void;
10
+ }
@@ -0,0 +1,80 @@
1
+ export default interface EspriInputNumberProps {
2
+ /**
3
+ * Label displayed above the input.
4
+ */
5
+ label: string;
6
+ /**
7
+ * Model value representing the current state of the input.
8
+ */
9
+ modelValue: number;
10
+ /**
11
+ * Class of the component.
12
+ */
13
+ class?: any;
14
+ /**
15
+ * Optional CSS class applied to the input element.
16
+ */
17
+ classInput?: string;
18
+ /**
19
+ * Disables the input when true.
20
+ */
21
+ disabled?: boolean;
22
+ /**
23
+ * Marks the input as required when true.
24
+ */
25
+ required?: boolean;
26
+ /**
27
+ * Step factor to increment/decrement the value.
28
+ */
29
+ step?: number;
30
+ /**
31
+ * The minimum number of fraction digits to use.
32
+ */
33
+ minFractionDigits?: number;
34
+ /**
35
+ * The maximum number of fraction digits to use.
36
+ */
37
+ maxFractionDigits?: number;
38
+ /**
39
+ * Indicates if the input is in a loading state.
40
+ */
41
+ loading: boolean;
42
+ /**
43
+ * Shows error messages when true.
44
+ */
45
+ showError?: boolean;
46
+ /**
47
+ * Defines the formatting mode for the input. Possible values are 'decimal' for standard number formatting and 'currency' for currency formatting.
48
+ */
49
+ mode?: 'decimal' | 'currency';
50
+ /**
51
+ * The currency to use in currency formatting. Possible values are the [ISO 4217 currency codes]
52
+ */
53
+ currency?: string;
54
+ /**
55
+ * Text to display after the value.
56
+ */
57
+ suffix?: string;
58
+ /**
59
+ * Minimum boundary value.
60
+ */
61
+ min?: number;
62
+ /**
63
+ * Maximum boundary value.
64
+ */
65
+ max?: number;
66
+ /**
67
+ * Validation rules for the input.
68
+ * Each rule can be undefined or an object containing:
69
+ * - $invalid: whether the rule is violated
70
+ * - $message: the error message associated with the rule
71
+ */
72
+ rules?: Array<{
73
+ $invalid: boolean;
74
+ $message: string;
75
+ } | undefined>;
76
+ /**
77
+ * Placeholder text displayed inside the input.
78
+ */
79
+ placeholder?: string;
80
+ }
@@ -6,7 +6,7 @@ export default interface EspriInputTextProps {
6
6
  /**
7
7
  * Model value representing the current state of the input.
8
8
  */
9
- modelValue: any;
9
+ modelValue: string;
10
10
  /**
11
11
  * Class of the component.
12
12
  */
@@ -1,7 +1,11 @@
1
1
  export interface EspriSelectOption {
2
- /** Display text for the option */
2
+ /**
3
+ * Display text for the option
4
+ */
3
5
  label: string;
4
- /** Value associated with the option */
6
+ /**
7
+ * Value associated with the option
8
+ */
5
9
  value: any;
6
10
  }
7
11
  export default interface EspriSelectProps {
@@ -0,0 +1,17 @@
1
+ import type { DefineComponent } from 'vue';
2
+ import type EspriVirtualKeyboardProps from './interfaces/virtual-keyboard-props.interface';
3
+ /**
4
+ * **ESPRI Digital - Virtual keyboard (Vue component)**
5
+ * --- ---
6
+ * ![ESPRIDigital](https://espridigital.fr/wp-content/uploads/2024/09/logo_espri_digital_white-100-1.png)
7
+ *
8
+ */
9
+ declare const EspriVirtualKeyboard: DefineComponent<EspriVirtualKeyboardProps, {}, // For slots if any
10
+ {}, // For methods or data if any
11
+ {}, // Computed
12
+ {}, // Methods
13
+ {}, // Mixin
14
+ {}, // Extends
15
+ {}>;
16
+ export default EspriVirtualKeyboard;
17
+ export type { EspriVirtualKeyboardProps };
@@ -0,0 +1,7 @@
1
+ export default interface EspriVirtualKeyboardProps {
2
+ /**
3
+ * Timestamp used to force the virtual keyboard to refresh its input focus listeners.
4
+ * Update this value in the parent to trigger the keyboard to rebind input events.
5
+ */
6
+ refreshInputsTimestamp?: number;
7
+ }
@@ -0,0 +1,159 @@
1
+ import { defineComponent as F, ref as u, onMounted as A, onBeforeUnmount as M, watch as R, openBlock as q, createElementBlock as P, normalizeClass as T, createElementVNode as V } from "vue";
2
+ import Y from "simple-keyboard";
3
+ const _ = "input:not(.not-open-keyboard), textarea:not(.not-open-keyboard)", x = "default-class", d = 230, U = /* @__PURE__ */ F({
4
+ __name: "virtual-keyboard",
5
+ props: {
6
+ refreshInputsTimestamp: {}
7
+ },
8
+ setup(r) {
9
+ const l = r, t = u(null), a = u(0), s = u(!0);
10
+ let v = 0;
11
+ const o = u(null), f = u();
12
+ let i = 0;
13
+ function C() {
14
+ return !!t.value?.className.includes("keyboard-force-dispatch-event");
15
+ }
16
+ function m() {
17
+ return t.value?.className.includes("keyboard-number") ? "numeric" : "text";
18
+ }
19
+ function I(e = !1) {
20
+ o.value && o.value.setOptions({
21
+ layout: {
22
+ default: [
23
+ `& é " ' ( § è ! ç à ) - {bksp}`,
24
+ "a z e r t y u i o p",
25
+ "q s d f g h j k l m",
26
+ "w x c v b n , ; : !",
27
+ "{shift} {space} {enter}"
28
+ ],
29
+ shift: [
30
+ "1 2 3 4 5 6 7 8 9 0 ° _ {bksp}",
31
+ "A Z E R T Y U I O P",
32
+ "Q S D F G H J K L M",
33
+ "W X C V B N ? . / §",
34
+ "{shift} {space} {enter}"
35
+ ]
36
+ },
37
+ newLineOnEnter: e
38
+ }), c();
39
+ }
40
+ function c() {
41
+ document.querySelectorAll("#keyboard .hg-rows, #keyboard .hg-row, #keyboard .hg-button").forEach((n) => {
42
+ n.removeEventListener("mousedown", g), n.addEventListener("mousedown", g), n.removeEventListener("touchstart", k), n.addEventListener("touchstart", k);
43
+ });
44
+ }
45
+ function B() {
46
+ o.value && o.value.setOptions({
47
+ layout: {
48
+ default: [
49
+ "1 2 3 {bksp}",
50
+ "4 5 6 {enter}",
51
+ "7 8 9 ",
52
+ ", 0 - "
53
+ ]
54
+ },
55
+ newLineOnEnter: !1
56
+ }), c();
57
+ }
58
+ function p() {
59
+ document.querySelectorAll(_).forEach((e) => {
60
+ e.addEventListener("focus", y), e.addEventListener("input", b);
61
+ });
62
+ }
63
+ function h(e) {
64
+ if (Date.now() - v < 100)
65
+ return;
66
+ const n = e.target;
67
+ t.value && !t.value.contains(n) && !f.value?.contains(n) && E();
68
+ }
69
+ function y(e) {
70
+ v = Date.now(), t.value = e.target, o.value && (o.value.setInput(t.value.value), D());
71
+ }
72
+ function b(e) {
73
+ o.value?.setInput(e.target.value);
74
+ }
75
+ function g(e) {
76
+ e.preventDefault();
77
+ }
78
+ function k(e) {
79
+ e.preventDefault();
80
+ }
81
+ function S() {
82
+ if (o.value) {
83
+ const n = o.value.options.layoutName === "default" ? "shift" : "default";
84
+ o.value.setOptions({
85
+ layoutName: n
86
+ }), c();
87
+ }
88
+ }
89
+ function E() {
90
+ s.value = !0, t.value = null, a.value = 0, document.body.style.marginBottom = `${i + a.value}px`;
91
+ }
92
+ function D() {
93
+ if (s.value = !1, t.value) {
94
+ m() === "numeric" ? B() : I(t.value.tagName.toLowerCase() === "textarea");
95
+ const e = t.value.getBoundingClientRect(), n = document.documentElement.scrollHeight;
96
+ n - e.bottom < d && (a.value = d - (n - e.bottom) + 30, i = +document.body.style.marginBottom.split("px")[0], document.body.style.marginBottom = `${i + a.value}px`);
97
+ }
98
+ setTimeout(() => {
99
+ if (t.value) {
100
+ const e = t.value.getBoundingClientRect(), n = document.documentElement.scrollHeight, L = window.innerHeight, N = window.scrollY + L - e.bottom, H = window.scrollY + (window.scrollY + (d - N)) + 30, O = n - L;
101
+ window.scrollTo({ top: Math.min(H, O), behavior: "smooth" });
102
+ }
103
+ }, 100);
104
+ }
105
+ function w() {
106
+ document.querySelectorAll(_).forEach((e) => {
107
+ e.removeEventListener("focus", y), e.removeEventListener("input", b);
108
+ });
109
+ }
110
+ function K() {
111
+ w(), p();
112
+ }
113
+ return A(async () => {
114
+ o.value = new Y(x, {
115
+ onChange: (e) => {
116
+ t.value && (t.value.value = e, (C() || m() === "text") && t.value.dispatchEvent(new Event("input")));
117
+ },
118
+ onKeyPress: (e) => {
119
+ if (t.value) {
120
+ const n = (t.value.selectionStart || 0) + (e === "{bksp}" ? -1 : 1);
121
+ setTimeout(() => {
122
+ t.value && t.value.setSelectionRange(n, n);
123
+ }, 0);
124
+ }
125
+ e === "{shift}" || e === "{lock}" ? S() : e === "{enter}" && t.value && t.value.tagName.toLowerCase() !== "textarea" && (t.value.blur(), E());
126
+ },
127
+ display: {
128
+ "{bksp}": "⌫",
129
+ "{enter}": "↵",
130
+ "{lock}": "⇪",
131
+ "{shift}": "⇧",
132
+ "{space}": " ",
133
+ "{tab}": "⇆"
134
+ }
135
+ }), p(), document.addEventListener("click", h);
136
+ }), M(() => {
137
+ w(), document.removeEventListener("click", h);
138
+ }), R(() => l.refreshInputsTimestamp, () => {
139
+ K();
140
+ }), (e, n) => (q(), P("div", {
141
+ id: "keyboard",
142
+ ref_key: "keyboardContainer",
143
+ ref: f,
144
+ class: T({ "hide-keyboard": s.value })
145
+ }, [
146
+ V("div", {
147
+ class: T(x)
148
+ })
149
+ ], 2));
150
+ }
151
+ }), z = (r, l) => {
152
+ const t = r.__vccOpts || r;
153
+ for (const [a, s] of l)
154
+ t[a] = s;
155
+ return t;
156
+ }, $ = /* @__PURE__ */ z(U, [["__scopeId", "data-v-7fa2df55"]]), J = $;
157
+ export {
158
+ J as default
159
+ };
@@ -0,0 +1 @@
1
+ .hg-theme-default{background-color:#ececec;border-radius:5px;box-sizing:border-box;font-family:HelveticaNeue-Light,Helvetica Neue Light,Helvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif;overflow:hidden;padding:5px;touch-action:manipulation;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%}.hg-theme-default .hg-button span,.hg-theme-default .hg-button span svg{pointer-events:none}.hg-theme-default button.hg-button{border-width:0;font-size:inherit}.hg-theme-default .hg-button{display:inline-block;flex-grow:1}.hg-theme-default .hg-row{display:flex}.hg-theme-default .hg-row:not(:last-child){margin-bottom:5px}.hg-theme-default .hg-row .hg-button-container,.hg-theme-default .hg-row .hg-button:not(:last-child){margin-right:5px}.hg-theme-default .hg-row>div:last-child{margin-right:0}.hg-theme-default .hg-row .hg-button-container{display:flex}.hg-theme-default .hg-button{align-items:center;background:#fff;border-bottom:1px solid #b5b5b5;border-radius:5px;box-shadow:0 0 3px -1px #0000004d;box-sizing:border-box;cursor:pointer;display:flex;height:40px;justify-content:center;padding:5px;-webkit-tap-highlight-color:rgba(0,0,0,0)}.hg-theme-default .hg-button.hg-standardBtn{width:20px}.hg-theme-default .hg-button.hg-activeButton{background:#efefef}.hg-theme-default.hg-layout-numeric .hg-button{align-items:center;display:flex;height:60px;justify-content:center;width:33.3%}.hg-theme-default .hg-button.hg-button-numpadadd,.hg-theme-default .hg-button.hg-button-numpadenter{height:85px}.hg-theme-default .hg-button.hg-button-numpad0{width:105px}.hg-theme-default .hg-button.hg-button-com{max-width:85px}.hg-theme-default .hg-button.hg-standardBtn.hg-button-at{max-width:45px}.hg-theme-default .hg-button.hg-selectedButton{background:#05194687;color:#fff}.hg-theme-default .hg-button.hg-standardBtn[data-skbtn=".com"]{max-width:82px}.hg-theme-default .hg-button.hg-standardBtn[data-skbtn="@"]{max-width:60px}.hg-candidate-box{background:#ececec;border-bottom:2px solid #b5b5b5;border-radius:5px;display:inline-flex;margin-top:-10px;position:absolute;transform:translateY(-100%);-webkit-user-select:none;-moz-user-select:none;user-select:none}ul.hg-candidate-box-list{display:flex;flex:1;list-style:none;margin:0;padding:0}li.hg-candidate-box-list-item{align-items:center;display:flex;height:40px;justify-content:center;width:40px}li.hg-candidate-box-list-item:hover{background:#00000008;cursor:pointer}li.hg-candidate-box-list-item:active{background:#0000001a}.hg-candidate-box-prev:before{content:"◄"}.hg-candidate-box-next:before{content:"►"}.hg-candidate-box-next,.hg-candidate-box-prev{align-items:center;color:#969696;cursor:pointer;display:flex;padding:0 10px}.hg-candidate-box-next{border-bottom-right-radius:5px;border-top-right-radius:5px}.hg-candidate-box-prev{border-bottom-left-radius:5px;border-top-left-radius:5px}.hg-candidate-box-btn-active{color:#444}#keyboard[data-v-7fa2df55]{position:fixed;bottom:0;left:0;width:100%}.hide-keyboard[data-v-7fa2df55]{display:none}.hg-button[data-skbtn="{shift}"],.hg-button[data-skbtn="{enter}"]{flex-grow:1;font-size:1.5em;width:20px}.hg-button[data-skbtn="{bksp}"]{width:20px}.hg-button[data-skbtn="{space}"]{flex-grow:8}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@espri/vue-components",
3
- "version": "1.1.0",
3
+ "version": "2.0.0",
4
4
  "description": "ESPRI Digital - Vue components library",
5
5
  "author": "ESPRI Digital",
6
6
  "homepage": "https://github.com/ESPRI-Digital/vue-select#readme",
@@ -12,6 +12,10 @@
12
12
  "types": "./dist/types/index.d.ts",
13
13
  "import": "./dist/index.es.js"
14
14
  },
15
+ "./input-number": {
16
+ "types": "./dist/types/input-number/index.d.ts",
17
+ "import": "./dist/input-number.es.js"
18
+ },
15
19
  "./input-text": {
16
20
  "types": "./dist/types/input-text/index.d.ts",
17
21
  "import": "./dist/input-text.es.js"
@@ -19,6 +23,10 @@
19
23
  "./select": {
20
24
  "types": "./dist/types/select/index.d.ts",
21
25
  "import": "./dist/select.es.js"
26
+ },
27
+ "./virtual-keyboard": {
28
+ "types": "./dist/types/virtual-keyboard/index.d.ts",
29
+ "import": "./dist/virtual-keyboard.es.js"
22
30
  }
23
31
  },
24
32
  "main": "dist/index.es.js",
@@ -38,6 +46,7 @@
38
46
  "@vitejs/plugin-vue": "^6.0.2",
39
47
  "eslint": "^9.39.1",
40
48
  "eslint-config-espri-front": "^3.0.0",
49
+ "simple-keyboard": "^3.0.0",
41
50
  "typescript": "^5.9.3",
42
51
  "vite": "^7.2.6",
43
52
  "vue-tsc": "^3.1.5"