letalkui 0.0.3 → 0.0.4
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/.github/workflows/npm-publish.yml +9 -0
- package/package.json +5 -1
- package/src/componentPatterns/JsonForm/ActionButton.tsx +35 -0
- package/src/componentPatterns/JsonForm/ActionsButtons.tsx +23 -0
- package/src/componentPatterns/JsonForm/Content.tsx +67 -0
- package/src/componentPatterns/JsonForm/Root.tsx +21 -0
- package/src/componentPatterns/JsonForm/index.ts +11 -0
- package/src/components/Button/Button.stories.ts +4 -3
- package/src/components/JsonEditor/JsonEditor.stories.ts +47 -0
- package/src/components/JsonEditor/index.tsx +84 -0
- package/src/components/JsonView/JsonView.stories.ts +42 -0
- package/src/components/JsonView/index.tsx +29 -0
- package/src/hooks/useValidation.tsx +39 -0
|
@@ -11,6 +11,8 @@ jobs:
|
|
|
11
11
|
steps:
|
|
12
12
|
- name: Checkout
|
|
13
13
|
uses: actions/checkout@v3
|
|
14
|
+
with:
|
|
15
|
+
fetch-depth: 0
|
|
14
16
|
|
|
15
17
|
- name: Setup Node.js
|
|
16
18
|
uses: actions/setup-node@v3
|
|
@@ -21,6 +23,13 @@ jobs:
|
|
|
21
23
|
- name: Install dependencies
|
|
22
24
|
run: npm i -f
|
|
23
25
|
|
|
26
|
+
- name: Bump version
|
|
27
|
+
run: |
|
|
28
|
+
git config --global user.name "github-actions[bot]"
|
|
29
|
+
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
|
30
|
+
npm version patch -m "chore(release): %s"
|
|
31
|
+
git push origin master --follow-tags
|
|
32
|
+
|
|
24
33
|
- name: Release
|
|
25
34
|
run: npm publish
|
|
26
35
|
env:
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "letalkui",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.4",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"lint": "eslint .",
|
|
@@ -10,7 +10,11 @@
|
|
|
10
10
|
"build:storybook": "storybook build"
|
|
11
11
|
},
|
|
12
12
|
"dependencies": {
|
|
13
|
+
"@emotion/react": "^11.14.0",
|
|
14
|
+
"@emotion/styled": "^11.14.0",
|
|
15
|
+
"@mui/material": "^6.4.3",
|
|
13
16
|
"react": "^18.3.1",
|
|
17
|
+
"react-ace": "^10.1.0",
|
|
14
18
|
"react-dom": "^18.3.1"
|
|
15
19
|
},
|
|
16
20
|
"devDependencies": {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
CircularProgress
|
|
6
|
+
} from "@mui/material"
|
|
7
|
+
|
|
8
|
+
type ActionButtonProps = {
|
|
9
|
+
onSave: () => void
|
|
10
|
+
loading: boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const ActionButton: React.FC<ActionButtonProps> = (props) => {
|
|
14
|
+
const {
|
|
15
|
+
onSave,
|
|
16
|
+
loading
|
|
17
|
+
} = props
|
|
18
|
+
|
|
19
|
+
const handleSave = () => {
|
|
20
|
+
onSave()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Button
|
|
25
|
+
onClick={handleSave}
|
|
26
|
+
color="primary"
|
|
27
|
+
variant="contained"
|
|
28
|
+
disableElevation
|
|
29
|
+
disabled={loading}
|
|
30
|
+
endIcon={loading && <CircularProgress size={20} color="inherit" />}
|
|
31
|
+
>
|
|
32
|
+
Salvar
|
|
33
|
+
</Button>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import {
|
|
3
|
+
Grid2 as Grid
|
|
4
|
+
} from "@mui/material"
|
|
5
|
+
|
|
6
|
+
type ActionsButtonsProps = {
|
|
7
|
+
children: React.ReactNode
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const ActionsButtons: React.FC<ActionsButtonsProps> = (props) => {
|
|
11
|
+
return (
|
|
12
|
+
<Grid
|
|
13
|
+
container
|
|
14
|
+
justify="flex-end"
|
|
15
|
+
>
|
|
16
|
+
<Grid
|
|
17
|
+
item
|
|
18
|
+
>
|
|
19
|
+
{props.children}
|
|
20
|
+
</Grid>
|
|
21
|
+
</Grid>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import AceEditor, { IAceEditorProps } from "react-ace"
|
|
3
|
+
import {
|
|
4
|
+
FormControl,
|
|
5
|
+
FormHelperText,
|
|
6
|
+
Grid2 as Grid
|
|
7
|
+
} from "@mui/material"
|
|
8
|
+
|
|
9
|
+
export type JsonValue = Record<string | number, unknown>
|
|
10
|
+
|
|
11
|
+
export type Overwrite<T, NewT> = Omit<T, keyof NewT> & NewT;
|
|
12
|
+
|
|
13
|
+
export type ContentProps = Overwrite<IAceEditorProps, {
|
|
14
|
+
value?: JsonValue | null
|
|
15
|
+
onChange?: (updatedJson: string) => void
|
|
16
|
+
currentError?: boolean
|
|
17
|
+
currentHelperText?: string
|
|
18
|
+
}>
|
|
19
|
+
|
|
20
|
+
const INPUT_NAME = "json-editor"
|
|
21
|
+
|
|
22
|
+
export const Content: React.FC<ContentProps> = (props) => {
|
|
23
|
+
const {
|
|
24
|
+
value,
|
|
25
|
+
onChange,
|
|
26
|
+
currentError,
|
|
27
|
+
currentHelperText,
|
|
28
|
+
...rest
|
|
29
|
+
} = props
|
|
30
|
+
|
|
31
|
+
const stringifyValue = JSON.stringify(value, null, 2)
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<Grid>
|
|
35
|
+
<FormControl
|
|
36
|
+
error={currentError}
|
|
37
|
+
fullWidth
|
|
38
|
+
>
|
|
39
|
+
<AceEditor
|
|
40
|
+
{...rest}
|
|
41
|
+
name={INPUT_NAME}
|
|
42
|
+
value={stringifyValue}
|
|
43
|
+
wrapEnabled={true}
|
|
44
|
+
mode="json"
|
|
45
|
+
theme="textmate"
|
|
46
|
+
onChange={onChange}
|
|
47
|
+
fontSize={16}
|
|
48
|
+
showPrintMargin={true}
|
|
49
|
+
showGutter={true}
|
|
50
|
+
highlightActiveLine={true}
|
|
51
|
+
editorProps={{ $blockScrolling: true }}
|
|
52
|
+
setOptions={{
|
|
53
|
+
showLineNumbers: true,
|
|
54
|
+
tabSize: 2
|
|
55
|
+
}}
|
|
56
|
+
/>
|
|
57
|
+
</FormControl>
|
|
58
|
+
{currentHelperText && (
|
|
59
|
+
<FormHelperText
|
|
60
|
+
error={currentError}
|
|
61
|
+
>
|
|
62
|
+
{currentHelperText}
|
|
63
|
+
</FormHelperText>
|
|
64
|
+
)}
|
|
65
|
+
</Grid>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Grid2 as Grid
|
|
5
|
+
} from "@mui/material"
|
|
6
|
+
|
|
7
|
+
type RootProps = {
|
|
8
|
+
children: React.ReactNode
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const Root: React.FC<RootProps> = (props) => {
|
|
12
|
+
return (
|
|
13
|
+
<Grid
|
|
14
|
+
container
|
|
15
|
+
direction="column"
|
|
16
|
+
spacing={2}
|
|
17
|
+
>
|
|
18
|
+
{props.children}
|
|
19
|
+
</Grid>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Root } from "@/componentPatterns/JsonForm/Root"
|
|
2
|
+
import { ActionButton } from "@/componentPatterns/JsonForm/ActionButton"
|
|
3
|
+
import { ActionsButtons } from "@/componentPatterns/JsonForm/ActionsButtons"
|
|
4
|
+
import { Content } from "@/componentPatterns/JsonForm/Content"
|
|
5
|
+
|
|
6
|
+
export const JsonForm = {
|
|
7
|
+
Root,
|
|
8
|
+
ActionButton,
|
|
9
|
+
ActionsButtons,
|
|
10
|
+
Content
|
|
11
|
+
}
|
|
@@ -3,7 +3,7 @@ import { fn } from "@storybook/test"
|
|
|
3
3
|
|
|
4
4
|
import { Button } from "@/components/Button"
|
|
5
5
|
|
|
6
|
-
const meta = {
|
|
6
|
+
const meta: Meta<typeof Button> = {
|
|
7
7
|
title: "Example/Button",
|
|
8
8
|
component: Button,
|
|
9
9
|
parameters: {
|
|
@@ -18,7 +18,7 @@ const meta = {
|
|
|
18
18
|
args: {
|
|
19
19
|
onClick: fn()
|
|
20
20
|
}
|
|
21
|
-
}
|
|
21
|
+
}
|
|
22
22
|
|
|
23
23
|
export default meta
|
|
24
24
|
|
|
@@ -27,7 +27,8 @@ type Story = StoryObj<typeof meta>
|
|
|
27
27
|
export const Primary: Story = {
|
|
28
28
|
args: {
|
|
29
29
|
primary: true,
|
|
30
|
-
label: "Button"
|
|
30
|
+
label: "Button",
|
|
31
|
+
size: "large"
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Meta,
|
|
3
|
+
StoryObj
|
|
4
|
+
} from "@storybook/react"
|
|
5
|
+
|
|
6
|
+
import { JsonEditor } from "@/components/JsonEditor"
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof JsonEditor> = {
|
|
9
|
+
title: "Example/JsonEditor",
|
|
10
|
+
component: JsonEditor,
|
|
11
|
+
parameters: {
|
|
12
|
+
layout: "centered"
|
|
13
|
+
},
|
|
14
|
+
tags: ["autodocs"],
|
|
15
|
+
argTypes: {
|
|
16
|
+
value: {
|
|
17
|
+
type: "string"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default meta
|
|
23
|
+
|
|
24
|
+
type Story = StoryObj<typeof meta>
|
|
25
|
+
|
|
26
|
+
export const Primary: Story = {
|
|
27
|
+
args: {
|
|
28
|
+
value: {
|
|
29
|
+
"id": 1,
|
|
30
|
+
"nome": "dsadsa",
|
|
31
|
+
"ativo": true,
|
|
32
|
+
"dataCriacao": "2025-02-07T12:00:00.000Z",
|
|
33
|
+
|
|
34
|
+
"itens": [{
|
|
35
|
+
"id": 101,
|
|
36
|
+
"descricao": "Item 1",
|
|
37
|
+
"quantidade": 10,
|
|
38
|
+
"preco": 19.99
|
|
39
|
+
}, {
|
|
40
|
+
"id": 102,
|
|
41
|
+
"descricao": "Item 2",
|
|
42
|
+
"quantidade": 5,
|
|
43
|
+
"preco": 49.99
|
|
44
|
+
}]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React, { useState } from "react"
|
|
2
|
+
import { IAceEditorProps } from "react-ace"
|
|
3
|
+
|
|
4
|
+
import { JsonForm } from "@/componentPatterns/JsonForm"
|
|
5
|
+
import {
|
|
6
|
+
JsonValue,
|
|
7
|
+
Overwrite
|
|
8
|
+
} from "@/componentPatterns/JsonForm/Content"
|
|
9
|
+
|
|
10
|
+
import useValidation from "@/hooks/useValidation"
|
|
11
|
+
|
|
12
|
+
type JsonEditorProps = Overwrite<IAceEditorProps, {
|
|
13
|
+
value?: JsonValue | null
|
|
14
|
+
onChange?: (value: string) => void
|
|
15
|
+
onSave?: (value: JsonValue) => Promise<JsonValue>
|
|
16
|
+
helperText?: string
|
|
17
|
+
error?: boolean
|
|
18
|
+
}>
|
|
19
|
+
|
|
20
|
+
const INPUT_NAME = "json-editor"
|
|
21
|
+
|
|
22
|
+
export const JsonEditor: React.FC<JsonEditorProps> = (props) => {
|
|
23
|
+
const {
|
|
24
|
+
value,
|
|
25
|
+
onChange,
|
|
26
|
+
onSave,
|
|
27
|
+
helperText,
|
|
28
|
+
error,
|
|
29
|
+
...rest
|
|
30
|
+
} = props
|
|
31
|
+
|
|
32
|
+
const { addValidation, clearValidation, validation } = useValidation()
|
|
33
|
+
|
|
34
|
+
const stringifyValue = JSON.stringify(value, null, 2)
|
|
35
|
+
|
|
36
|
+
const [jsonData, setJsonData] = useState<string>(stringifyValue)
|
|
37
|
+
const [loadingSaveJson, setLoadingSaveJson] = useState<boolean>(false)
|
|
38
|
+
|
|
39
|
+
const currentError: boolean = validation[INPUT_NAME] ?? error
|
|
40
|
+
const currentHelperText: string = validation[INPUT_NAME] ?? helperText
|
|
41
|
+
|
|
42
|
+
const handleChange = (newValue: string) => {
|
|
43
|
+
clearValidation(INPUT_NAME)
|
|
44
|
+
|
|
45
|
+
setJsonData(newValue)
|
|
46
|
+
|
|
47
|
+
onChange?.(newValue)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const handleSave = async () => {
|
|
51
|
+
setLoadingSaveJson(true)
|
|
52
|
+
|
|
53
|
+
if (onSave) {
|
|
54
|
+
try {
|
|
55
|
+
const parseJsonData = JSON.parse(jsonData)
|
|
56
|
+
|
|
57
|
+
await onSave(parseJsonData)
|
|
58
|
+
} catch (error) {
|
|
59
|
+
addValidation({ [INPUT_NAME]: error })
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
setLoadingSaveJson(false)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<JsonForm.Root>
|
|
68
|
+
<JsonForm.Content
|
|
69
|
+
{...rest}
|
|
70
|
+
value={value}
|
|
71
|
+
onChange={handleChange}
|
|
72
|
+
currentError={currentError}
|
|
73
|
+
currentHelperText={currentHelperText}
|
|
74
|
+
/>
|
|
75
|
+
|
|
76
|
+
<JsonForm.ActionsButtons>
|
|
77
|
+
<JsonForm.ActionButton
|
|
78
|
+
onSave={handleSave}
|
|
79
|
+
loading={loadingSaveJson}
|
|
80
|
+
/>
|
|
81
|
+
</JsonForm.ActionsButtons>
|
|
82
|
+
</JsonForm.Root>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Meta,
|
|
3
|
+
StoryObj
|
|
4
|
+
} from "@storybook/react"
|
|
5
|
+
|
|
6
|
+
import { JsonView } from "@/components/JsonView"
|
|
7
|
+
|
|
8
|
+
const meta: Meta<typeof JsonView> = {
|
|
9
|
+
title: "Example/JsonView",
|
|
10
|
+
component: JsonView,
|
|
11
|
+
parameters: {
|
|
12
|
+
layout: "centered"
|
|
13
|
+
},
|
|
14
|
+
tags: ["autodocs"]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default meta
|
|
18
|
+
|
|
19
|
+
type Story = StoryObj<typeof meta>
|
|
20
|
+
|
|
21
|
+
export const Primary: Story = {
|
|
22
|
+
args: {
|
|
23
|
+
value: {
|
|
24
|
+
"id": 1,
|
|
25
|
+
"nome": "dsadsa",
|
|
26
|
+
"ativo": true,
|
|
27
|
+
"dataCriacao": "2025-02-07T12:00:00.000Z",
|
|
28
|
+
|
|
29
|
+
"itens": [{
|
|
30
|
+
"id": 101,
|
|
31
|
+
"descricao": "Item 1",
|
|
32
|
+
"quantidade": 10,
|
|
33
|
+
"preco": 19.99
|
|
34
|
+
}, {
|
|
35
|
+
"id": 102,
|
|
36
|
+
"descricao": "Item 2",
|
|
37
|
+
"quantidade": 5,
|
|
38
|
+
"preco": 49.99
|
|
39
|
+
}]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { IAceEditorProps } from "react-ace"
|
|
3
|
+
|
|
4
|
+
import { JsonForm } from "@/componentPatterns/JsonForm"
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
JsonValue,
|
|
8
|
+
Overwrite
|
|
9
|
+
} from "@/componentPatterns/JsonForm/Content"
|
|
10
|
+
|
|
11
|
+
type JsonViewProps = Overwrite<IAceEditorProps, {
|
|
12
|
+
value?: JsonValue | null
|
|
13
|
+
}>
|
|
14
|
+
|
|
15
|
+
export const JsonView: React.FC<JsonViewProps> = (props) => {
|
|
16
|
+
const {
|
|
17
|
+
value,
|
|
18
|
+
...rest
|
|
19
|
+
} = props
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<JsonForm.Root>
|
|
23
|
+
<JsonForm.Content
|
|
24
|
+
{...rest}
|
|
25
|
+
value={value}
|
|
26
|
+
/>
|
|
27
|
+
</JsonForm.Root>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { useState } from "react"
|
|
2
|
+
|
|
3
|
+
export type ValidationType = Record<string, unknown>
|
|
4
|
+
|
|
5
|
+
const useValidation = () => {
|
|
6
|
+
const [validation, setValidation] = useState<ValidationType>({})
|
|
7
|
+
|
|
8
|
+
const addValidation = (newValidation: ValidationType) => {
|
|
9
|
+
setValidation((lastValidation) => ({
|
|
10
|
+
...lastValidation,
|
|
11
|
+
...newValidation
|
|
12
|
+
}))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const clearValidation = (key: string) => {
|
|
16
|
+
if (key in validation) {
|
|
17
|
+
setValidation((lastValidation) => {
|
|
18
|
+
const updatedData: ValidationType = { ...lastValidation }
|
|
19
|
+
|
|
20
|
+
delete updatedData[key]
|
|
21
|
+
|
|
22
|
+
return updatedData
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const clearAllValidations = () => {
|
|
28
|
+
setValidation({})
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
addValidation,
|
|
33
|
+
clearValidation,
|
|
34
|
+
clearAllValidations,
|
|
35
|
+
validation
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default useValidation
|