kennzeichen 0.1.0 → 0.1.3
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 +27 -82
- package/package.json +31 -31
package/README.md
CHANGED
|
@@ -18,44 +18,22 @@ npm install kennzeichen
|
|
|
18
18
|
|
|
19
19
|
## Usage
|
|
20
20
|
|
|
21
|
-
### Core
|
|
22
|
-
|
|
23
|
-
The core module works in any JavaScript environment (Node.js, browser, Deno, etc.).
|
|
21
|
+
### Core
|
|
24
22
|
|
|
25
23
|
```typescript
|
|
26
|
-
import {
|
|
27
|
-
parseLicensePlate,
|
|
28
|
-
formatParsedPlate,
|
|
29
|
-
isValidLicensePlate,
|
|
30
|
-
sanitizeLicensePlate,
|
|
31
|
-
LOCATION,
|
|
32
|
-
} from "kennzeichen";
|
|
24
|
+
import { parseLicensePlate, isValidLicensePlate } from "kennzeichen";
|
|
33
25
|
|
|
34
26
|
// Parse a license plate
|
|
35
|
-
|
|
27
|
+
parseLicensePlate("M-AB 1234");
|
|
36
28
|
// { type: 'unambiguous', plate: { part1: 'M', part2: 'AB', part3: '1234' } }
|
|
37
29
|
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
// { type: 'ambiguous', options: [
|
|
41
|
-
// { part1: 'C', part2: 'EE', part3: '1234' },
|
|
42
|
-
// { part1: 'CE', part2: 'E', part3: '1234' }
|
|
43
|
-
// ] }
|
|
44
|
-
|
|
45
|
-
// Format a parsed plate
|
|
46
|
-
formatParsedPlate({ part1: "M", part2: "AB", part3: "1234" });
|
|
47
|
-
// 'M-AB 1234'
|
|
30
|
+
// Ambiguous input returns multiple options
|
|
31
|
+
parseLicensePlate("CEE1234");
|
|
32
|
+
// { type: 'ambiguous', options: [...] }
|
|
48
33
|
|
|
49
34
|
// Validate
|
|
50
35
|
isValidLicensePlate("M-AB 1234"); // true
|
|
51
|
-
isValidLicensePlate("XX-AB 1234"); // false
|
|
52
|
-
|
|
53
|
-
// Sanitize (remove separators)
|
|
54
|
-
sanitizeLicensePlate("M-AB 1234"); // 'MAB1234'
|
|
55
|
-
|
|
56
|
-
// Check if a location code exists
|
|
57
|
-
LOCATION.includes("M"); // true
|
|
58
|
-
LOCATION.includes("XX"); // false
|
|
36
|
+
isValidLicensePlate("XX-AB 1234"); // false
|
|
59
37
|
```
|
|
60
38
|
|
|
61
39
|
### Parse Result Types
|
|
@@ -76,40 +54,22 @@ type ParsedPlate = {
|
|
|
76
54
|
|
|
77
55
|
### React Hook
|
|
78
56
|
|
|
79
|
-
The hook provides full control over the input state and disambiguation logic.
|
|
80
|
-
|
|
81
57
|
```tsx
|
|
82
58
|
import { useLicensePlate, formatParsedPlate } from "kennzeichen/react";
|
|
83
59
|
|
|
84
|
-
function
|
|
85
|
-
const
|
|
60
|
+
function LicensePlateInput({ value, onChange }) {
|
|
61
|
+
const { inputValue, handleChange, isDropdownOpen, options, selectOption } =
|
|
62
|
+
useLicensePlate({ value, onChange });
|
|
86
63
|
|
|
87
64
|
return (
|
|
88
|
-
<div
|
|
89
|
-
<input
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
{lp.isDropdownOpen && lp.options && (
|
|
97
|
-
<ul className="absolute mt-1 border rounded bg-white shadow-lg">
|
|
98
|
-
{lp.options.map((opt, i) => (
|
|
99
|
-
<li
|
|
100
|
-
key={i}
|
|
101
|
-
className={`px-3 py-2 cursor-pointer ${
|
|
102
|
-
i === lp.activeIndex ? "bg-blue-100" : "hover:bg-gray-100"
|
|
103
|
-
}`}
|
|
104
|
-
onMouseEnter={() => lp.setActiveIndex(i)}
|
|
105
|
-
onMouseDown={(e) => e.preventDefault()}
|
|
106
|
-
onClick={() => lp.selectOption(opt)}
|
|
107
|
-
>
|
|
108
|
-
{formatParsedPlate(opt)}
|
|
109
|
-
</li>
|
|
110
|
-
))}
|
|
111
|
-
</ul>
|
|
112
|
-
)}
|
|
65
|
+
<div>
|
|
66
|
+
<input value={inputValue} onChange={handleChange} />
|
|
67
|
+
{isDropdownOpen &&
|
|
68
|
+
options?.map((opt, i) => (
|
|
69
|
+
<button key={i} onClick={() => selectOption(opt)}>
|
|
70
|
+
{formatParsedPlate(opt)}
|
|
71
|
+
</button>
|
|
72
|
+
))}
|
|
113
73
|
</div>
|
|
114
74
|
);
|
|
115
75
|
}
|
|
@@ -117,36 +77,21 @@ function MyLicensePlateInput({ value, onChange }) {
|
|
|
117
77
|
|
|
118
78
|
### React Headless Component
|
|
119
79
|
|
|
120
|
-
The headless component uses render props to reduce boilerplate while still giving you full control over the UI.
|
|
121
|
-
|
|
122
80
|
```tsx
|
|
123
81
|
import { LicensePlateInput } from "kennzeichen/react";
|
|
124
82
|
|
|
125
|
-
function
|
|
83
|
+
function MyInput({ value, onChange }) {
|
|
126
84
|
return (
|
|
127
85
|
<LicensePlateInput value={value} onChange={onChange}>
|
|
128
86
|
{({ inputProps, isOpen, options }) => (
|
|
129
|
-
<div
|
|
130
|
-
<input
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
{options.map((opt) => (
|
|
138
|
-
<li
|
|
139
|
-
key={opt.formatted}
|
|
140
|
-
className={`px-3 py-2 cursor-pointer ${
|
|
141
|
-
opt.isActive ? "bg-blue-100" : "hover:bg-gray-100"
|
|
142
|
-
}`}
|
|
143
|
-
{...opt.getProps()}
|
|
144
|
-
>
|
|
145
|
-
{opt.formatted}
|
|
146
|
-
</li>
|
|
147
|
-
))}
|
|
148
|
-
</ul>
|
|
149
|
-
)}
|
|
87
|
+
<div>
|
|
88
|
+
<input {...inputProps} />
|
|
89
|
+
{isOpen &&
|
|
90
|
+
options?.map((opt) => (
|
|
91
|
+
<button key={opt.formatted} {...opt.getProps()}>
|
|
92
|
+
{opt.formatted}
|
|
93
|
+
</button>
|
|
94
|
+
))}
|
|
150
95
|
</div>
|
|
151
96
|
)}
|
|
152
97
|
</LicensePlateInput>
|
package/package.json
CHANGED
|
@@ -1,21 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kennzeichen",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "German license plate parsing and validation",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"german",
|
|
7
|
+
"license-plate",
|
|
8
|
+
"kennzeichen",
|
|
9
|
+
"parser",
|
|
10
|
+
"validation"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/canida-software/kennzeichen#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/canida-software/kennzeichen/issues"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/canida-software/kennzeichen.git"
|
|
19
|
+
},
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"author": "Canida Software <bui.qd@canida.io>",
|
|
5
22
|
"type": "module",
|
|
6
23
|
"sideEffects": false,
|
|
7
24
|
"exports": {
|
|
8
25
|
".": {
|
|
9
|
-
"
|
|
10
|
-
"
|
|
26
|
+
"types": "./dist/core/index.d.ts",
|
|
27
|
+
"import": "./dist/core/index.js"
|
|
11
28
|
},
|
|
12
29
|
"./core": {
|
|
13
|
-
"
|
|
14
|
-
"
|
|
30
|
+
"types": "./dist/core/index.d.ts",
|
|
31
|
+
"import": "./dist/core/index.js"
|
|
15
32
|
},
|
|
16
33
|
"./react": {
|
|
17
|
-
"
|
|
18
|
-
"
|
|
34
|
+
"types": "./dist/react/index.d.ts",
|
|
35
|
+
"import": "./dist/react/index.js"
|
|
19
36
|
}
|
|
20
37
|
},
|
|
21
38
|
"files": [
|
|
@@ -23,8 +40,8 @@
|
|
|
23
40
|
],
|
|
24
41
|
"scripts": {
|
|
25
42
|
"build": "tsup",
|
|
26
|
-
"test": "vitest run",
|
|
27
43
|
"lint": "oxlint -D recommended --ignore-pattern node_modules",
|
|
44
|
+
"test": "vitest run",
|
|
28
45
|
"typecheck": "tsc --noEmit"
|
|
29
46
|
},
|
|
30
47
|
"peerDependencies": {
|
|
@@ -36,28 +53,11 @@
|
|
|
36
53
|
}
|
|
37
54
|
},
|
|
38
55
|
"devDependencies": {
|
|
39
|
-
"@types/react": "^
|
|
40
|
-
"oxlint": "^
|
|
41
|
-
"react": "^
|
|
42
|
-
"tsup": "^8.
|
|
43
|
-
"typescript": "^5.
|
|
44
|
-
"vitest": "^
|
|
45
|
-
},
|
|
46
|
-
"keywords": [
|
|
47
|
-
"german",
|
|
48
|
-
"license-plate",
|
|
49
|
-
"kennzeichen",
|
|
50
|
-
"parser",
|
|
51
|
-
"validation"
|
|
52
|
-
],
|
|
53
|
-
"license": "MIT",
|
|
54
|
-
"author": "Canida Software <bui.qd@canida.io>",
|
|
55
|
-
"repository": {
|
|
56
|
-
"type": "git",
|
|
57
|
-
"url": "git+https://github.com/canida-software/kennzeichen.git"
|
|
58
|
-
},
|
|
59
|
-
"homepage": "https://github.com/canida-software/kennzeichen#readme",
|
|
60
|
-
"bugs": {
|
|
61
|
-
"url": "https://github.com/canida-software/kennzeichen/issues"
|
|
56
|
+
"@types/react": "^19.2.9",
|
|
57
|
+
"oxlint": "^1.41.0",
|
|
58
|
+
"react": "^19.2.3",
|
|
59
|
+
"tsup": "^8.5.1",
|
|
60
|
+
"typescript": "^5.9.3",
|
|
61
|
+
"vitest": "^4.0.18"
|
|
62
62
|
}
|
|
63
63
|
}
|