@stonecrop/aform 0.2.5 → 0.2.6

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,8 +1,20 @@
1
1
  {
2
2
  "name": "@stonecrop/aform",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
+ "author": {
7
+ "name": "Tyler Matteson",
8
+ "email": "tyler@agritheory.com"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/agritheory/stonecrop",
13
+ "directory": "aform"
14
+ },
15
+ "bugs": {
16
+ "url": "https://github.com/agritheory/stonecrop/issues"
17
+ },
6
18
  "exports": {
7
19
  ".": {
8
20
  "import": "./dist/aform.js",
@@ -12,45 +24,48 @@
12
24
  },
13
25
  "main": "dist/aform.js",
14
26
  "module": "dist/aform.js",
27
+ "umd": "dist/aform.umd.cjs",
15
28
  "types": "src/index",
16
29
  "files": [
17
30
  "dist/*",
18
- "src/**/*.vue"
31
+ "src/*"
19
32
  ],
20
33
  "dependencies": {
21
34
  "uuid": "^9.0.0",
22
- "vue": "^3.2.47",
23
- "@stonecrop/themes": "0.2.5",
24
- "@stonecrop/utilities": "0.2.5"
35
+ "vue": "^3.4.23",
36
+ "@stonecrop/utilities": "0.2.6",
37
+ "@stonecrop/themes": "0.2.6"
25
38
  },
26
39
  "devDependencies": {
27
- "@histoire/plugin-vue": "^0.16.1",
40
+ "@histoire/plugin-vue": "^0.17.17",
28
41
  "@types/uuid": "^9.0.0",
29
42
  "@typescript-eslint/eslint-plugin": "^5.59.5",
30
43
  "@typescript-eslint/parser": "^5.59.5",
31
- "@vitejs/plugin-vue": "^4.2.1",
32
- "@vitest/coverage-c8": "^0.31.0",
33
- "@vitest/ui": "^0.31.0",
44
+ "@vitejs/plugin-vue": "^5.0.4",
45
+ "@vitest/coverage-v8": "^1.5.0",
46
+ "@vitest/ui": "^1.5.0",
34
47
  "@vue/test-utils": "^2.3.2",
35
48
  "cypress": "^12.11.0",
36
49
  "eslint": "^8.40.0",
37
50
  "eslint-config-prettier": "^8.8.0",
38
51
  "eslint-plugin-vue": "^9.11.1",
39
- "histoire": "^0.16.1",
52
+ "histoire": "^0.17.17",
40
53
  "jsdom": "^22.0.0",
41
54
  "typescript": "^5.0.4",
42
- "vite": "^4.3.5",
43
- "vitest": "^0.31.0",
55
+ "vite": "^5.2.9",
56
+ "vitest": "^1.5.0",
44
57
  "vue-router": "^4",
45
- "@stonecrop/atable": "0.2.5"
58
+ "@stonecrop/atable": "0.2.6"
46
59
  },
47
60
  "peerDependencies": {
48
- "@stonecrop/atable": "0.2.5"
61
+ "@stonecrop/atable": "0.2.6"
62
+ },
63
+ "publishConfig": {
64
+ "access": "public"
49
65
  },
50
66
  "engines": {
51
67
  "node": ">=20.11.0"
52
68
  },
53
- "umd": "dist/aform.umd.cjs",
54
69
  "scripts": {
55
70
  "build": "tsc -b && vite build",
56
71
  "dev": "vite",
@@ -10,12 +10,11 @@
10
10
  </template>
11
11
 
12
12
  <script setup lang="ts">
13
- import { computed, InputHTMLAttributes } from 'vue'
13
+ import { InputHTMLAttributes } from 'vue'
14
14
 
15
- const props = withDefaults(
15
+ withDefaults(
16
16
  defineProps<{
17
17
  label?: string
18
- value?: InputHTMLAttributes['checked']
19
18
  required?: boolean
20
19
  readOnly?: boolean
21
20
  uuid?: string
@@ -26,18 +25,7 @@ const props = withDefaults(
26
25
  }
27
26
  )
28
27
 
29
- const emit = defineEmits<{
30
- (e: 'update:value', value: InputHTMLAttributes['checked']): void
31
- }>()
32
-
33
- const checkbox = computed({
34
- get() {
35
- return props.value
36
- },
37
- set(value) {
38
- emit('update:value', value)
39
- },
40
- })
28
+ const checkbox = defineModel<InputHTMLAttributes['checked']>()
41
29
  </script>
42
30
 
43
31
  <style scoped>
@@ -7,12 +7,9 @@
7
7
  </template>
8
8
 
9
9
  <script setup lang="ts">
10
- import { computed } from 'vue'
11
-
12
- const props = withDefaults(
10
+ withDefaults(
13
11
  defineProps<{
14
12
  label: string
15
- modelValue: any
16
13
  required?: boolean
17
14
  readonly?: boolean
18
15
  uuid?: string
@@ -22,15 +19,8 @@ const props = withDefaults(
22
19
  validation: () => ({ errorMessage: '&nbsp;' }),
23
20
  }
24
21
  )
25
- const emit = defineEmits(['update:modelValue'])
26
- const inputNumber = computed({
27
- get: () => {
28
- return props.modelValue
29
- },
30
- set: newValue => {
31
- emit('update:modelValue', newValue)
32
- },
33
- })
22
+
23
+ const inputNumber = defineModel<number>()
34
24
  </script>
35
25
 
36
26
  <style scoped>
@@ -0,0 +1,104 @@
1
+ import type { FormSchema } from 'types'
2
+ import { DirectiveBinding } from 'vue'
3
+
4
+ const NAMED_MASKS = {
5
+ date: '##/##/####',
6
+ datetime: '####/##/## ##:##',
7
+ time: '##:##',
8
+ fulltime: '##:##:##',
9
+ phone: '(###) ### - ####',
10
+ card: '#### #### #### ####',
11
+ }
12
+
13
+ function extractMaskFn(mask: string): ((args: any) => string) | void {
14
+ try {
15
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
16
+ return Function(`"use strict";return (${mask})`)()
17
+ } catch (error) {
18
+ if (error instanceof ReferenceError) {
19
+ // assume mask is a string
20
+ }
21
+ }
22
+ }
23
+
24
+ function getMask(binding: DirectiveBinding<string>) {
25
+ let mask = binding.value
26
+
27
+ if (mask) {
28
+ const maskFn = extractMaskFn(mask)
29
+ if (maskFn) {
30
+ // TODO: (state) replace with state management;
31
+ // pass the entire form/table data to the function
32
+ const locale = binding.instance['locale']
33
+ mask = maskFn(locale)
34
+ }
35
+ } else {
36
+ // TODO: (state) handle using state management
37
+ const schema: FormSchema = binding.instance['schema']
38
+ const fieldType: string | undefined = schema.fieldtype?.toLowerCase()
39
+ if (fieldType && NAMED_MASKS[fieldType]) {
40
+ mask = NAMED_MASKS[fieldType]
41
+ }
42
+ }
43
+
44
+ return mask
45
+ }
46
+
47
+ function unmaskInput(input: string, maskToken?: string) {
48
+ if (!maskToken) {
49
+ maskToken = '#'
50
+ }
51
+
52
+ let unmaskedInput = input
53
+ const maskChars = [maskToken, '/', '-', '(', ')', ' ']
54
+
55
+ for (const char of maskChars) {
56
+ unmaskedInput = unmaskedInput.replaceAll(char, '')
57
+ }
58
+
59
+ return unmaskedInput
60
+ }
61
+
62
+ function fillMask(input: string, mask: string, maskToken?: string) {
63
+ if (!maskToken) {
64
+ maskToken = '#'
65
+ }
66
+
67
+ let replacement = mask
68
+ for (const inputChar of input) {
69
+ const replaceIndex = replacement.indexOf(maskToken)
70
+ if (replaceIndex !== -1) {
71
+ const prefix = replacement.substring(0, replaceIndex)
72
+ const suffix = replacement.substring(replaceIndex + 1)
73
+ replacement = prefix + inputChar + suffix
74
+ }
75
+ }
76
+
77
+ return replacement.slice(0, mask.length)
78
+ }
79
+
80
+ export function useStringMask(el: HTMLInputElement, binding: DirectiveBinding<string>) {
81
+ const mask = getMask(binding)
82
+ if (!mask) return
83
+
84
+ const maskToken = '#'
85
+ const inputText = el.value
86
+
87
+ // process input value with mask
88
+ const unmaskedInput = unmaskInput(inputText, maskToken)
89
+ if (unmaskedInput) {
90
+ const replacement = fillMask(unmaskedInput, mask, maskToken)
91
+
92
+ // TODO: (state) this is very opinionated;
93
+ // most likely fixed with state management;
94
+ // a better way could be to emit back to instance;
95
+
96
+ if (binding.instance['maskFilled']) {
97
+ binding.instance['maskFilled'] = !replacement.includes(maskToken)
98
+ }
99
+
100
+ el.value = replacement
101
+ } else {
102
+ el.value = mask
103
+ }
104
+ }
@@ -0,0 +1,22 @@
1
+ import { defineSetupVue3 } from '@histoire/plugin-vue'
2
+
3
+ import ACheckbox from '@/components/form/ACheckbox.vue'
4
+ import AFieldset from '@/components/form/AFieldset.vue'
5
+ import AForm from '@/components/AForm.vue'
6
+ import ANumericInput from '@/components/form/ANumericInput.vue'
7
+ import ATextInput from '@/components/form/ATextInput.vue'
8
+
9
+ import { ATable, ATableHeader, ATableModal } from '@stonecrop/atable'
10
+ import '@stonecrop/atable/styles'
11
+
12
+ export const setupVue3 = defineSetupVue3(({ app }) => {
13
+ // TODO: (typing) add typing for ATable components
14
+ app.component('ACheckbox', ACheckbox)
15
+ app.component('AFieldset', AFieldset)
16
+ app.component('AForm', AForm)
17
+ app.component('ANumericInput', ANumericInput)
18
+ app.component('ATable', ATable)
19
+ app.component('ATableHeader', ATableHeader)
20
+ app.component('ATableModal', ATableModal)
21
+ app.component('ATextInput', ATextInput)
22
+ })
package/src/index.ts ADDED
@@ -0,0 +1,27 @@
1
+ import { App } from 'vue'
2
+
3
+ import ACheckbox from '@/components/form/ACheckbox.vue'
4
+ import AComboBox from '@/components/form/AComboBox.vue'
5
+ import ADate from '@/components/form/ADate.vue'
6
+ import ADropdown from '@/components/form/ADropdown.vue'
7
+ import AFieldset from '@/components/form/AFieldset.vue'
8
+ import AForm from '@/components/AForm.vue'
9
+ import ANumericInput from '@/components/form/ANumericInput.vue'
10
+ import ATextInput from '@/components/form/ATextInput.vue'
11
+ // import { ACurrency } from '@/components/form/ACurrency.vue'
12
+ // import { AQuantity } from '@/components/form/AQuantity.vue'
13
+
14
+ function install(app: App /* options */) {
15
+ app.component('ACheckbox', ACheckbox)
16
+ app.component('ACombobox', AComboBox)
17
+ app.component('ADate', ADate)
18
+ app.component('ADropdown', ADropdown)
19
+ app.component('AFieldset', AFieldset)
20
+ app.component('AForm', AForm)
21
+ app.component('ANumericInput', ANumericInput)
22
+ app.component('ATextInput', ATextInput)
23
+ // app.component('ACurrency', ACurrency)
24
+ // app.component('AQuantity', AQuantity)
25
+ }
26
+
27
+ export { ACheckbox, AComboBox, ADate, ADropdown, AFieldset, AForm, ANumericInput, ATextInput, install }
@@ -0,0 +1,5 @@
1
+ declare module '*.vue' {
2
+ import { ComponentOptions } from 'vue'
3
+ const Component: ComponentOptions
4
+ export default Component
5
+ }
@@ -0,0 +1,28 @@
1
+ @import url('https://fonts.googleapis.com/css2?family=Arimo:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&display=swap');
2
+ @import url('@stonecrop/themes/default/default.css');
3
+ @import url('custom_themes.css');
4
+
5
+ :root {
6
+ font-family: var(--font-family);
7
+ }
8
+
9
+ .aform-primary-action {
10
+ font-size: 100%;
11
+ text-align: center;
12
+ min-height: 2em;
13
+ padding: 0.25rem 1rem 0.25rem 1rem;
14
+ border: 1px solid var(--primary-color);
15
+ color: var(--primary-text-color);
16
+ background-color: var(--primary-color);
17
+ outline: 2px solid var(--primary-text-color);
18
+ transition: outline-offset 200ms ease;
19
+ font-size: var(--font-size);
20
+ margin: 0.5ch;
21
+ }
22
+
23
+ .aform-primary-action:hover,
24
+ .aform-primary-action:active {
25
+ outline: 2px solid var(--primary-text-color);
26
+ outline-offset: -4px;
27
+ transition: outline-offset 200ms ease;
28
+ }
@@ -0,0 +1,6 @@
1
+ /*
2
+
3
+ Import custom themes here, e.g.
4
+ @import url('purple_theme.css');
5
+
6
+ */
@@ -0,0 +1,9 @@
1
+ @import url('https://fonts.googleapis.com/css2?family=Arimo:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&display=swap');
2
+
3
+ :root {
4
+ --primary-color: #645caa;
5
+ }
6
+
7
+ :root [data-theme='purple'] {
8
+ --row-color-zebra-light: #ebc7e8;
9
+ }