@oslokommune/punkt-react 13.2.4 → 13.3.1
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/CHANGELOG.md +1329 -1038
- package/README.md +1 -1
- package/dist/index.d.ts +32 -14
- package/dist/punkt-react.es.js +43127 -23133
- package/dist/punkt-react.umd.js +527 -507
- package/package.json +6 -8
- package/src/components/index.ts +5 -3
- package/src/components/linkcard/LinkCard.tsx +1 -1
- package/src/components/preview/Preview.tsx +4 -2
- package/src/components/preview/PreviewCode.tsx +118 -0
- package/src/components/preview/PreviewPropEditor.tsx +165 -113
- package/src/components/preview/PreviewSpecs.tsx +2 -24
- package/src/components/preview/PreviewWithJson.tsx +519 -0
- package/src/components/preview/preview-types.ts +42 -0
- package/src/components/preview/preview-utils.ts +226 -0
- package/src/components/preview/previewJson/accordion.json +84 -0
- package/src/components/preview/previewJson/alert.json +27 -0
- package/src/components/preview/previewJson/backlink.json +14 -0
- package/src/components/preview/previewJson/breadcrumbs.json +17 -0
- package/src/components/preview/previewJson/button.json +28 -0
- package/src/components/preview/previewJson/card.json +41 -0
- package/src/components/preview/previewJson/checkbox.json +21 -0
- package/src/components/preview/previewJson/combobox.json +24 -0
- package/src/components/preview/previewJson/consent.json +14 -0
- package/src/components/preview/previewJson/datepicker.json +14 -0
- package/src/components/preview/previewJson/footer-simple.json +17 -0
- package/src/components/preview/previewJson/footer.json +29 -0
- package/src/components/preview/previewJson/header.json +34 -0
- package/src/components/preview/previewJson/icon.json +13 -0
- package/src/components/preview/previewJson/input-wrapper.json +39 -0
- package/src/components/preview/previewJson/link.json +28 -0
- package/src/components/preview/previewJson/linkcard.json +30 -0
- package/src/components/preview/previewJson/loader.json +28 -0
- package/src/components/preview/previewJson/messagebox.json +28 -0
- package/src/components/preview/previewJson/modal.json +65 -0
- package/src/components/preview/previewJson/progressbar.json +16 -0
- package/src/components/preview/previewJson/radiobutton.json +21 -0
- package/src/components/preview/previewJson/searchinput.json +20 -0
- package/src/components/preview/previewJson/select.json +73 -0
- package/src/components/preview/previewJson/steps.json +72 -0
- package/src/components/preview/previewJson/table.json +130 -0
- package/src/components/preview/previewJson/tabs.json +33 -0
- package/src/components/preview/previewJson/tag.json +26 -0
- package/src/components/preview/previewJson/textarea.json +18 -0
- package/src/components/preview/previewJson/textinput.json +18 -0
- package/src/components/searchinput/SearchInput.tsx +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oslokommune/punkt-react",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.3.1",
|
|
4
4
|
"description": "React komponentbibliotek til Punkt, et designsystem laget av Oslo Origo",
|
|
5
5
|
"homepage": "https://punkt.oslo.kommune.no",
|
|
6
6
|
"author": "Team Designsystem, Oslo Origo",
|
|
@@ -38,18 +38,16 @@
|
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@lit-labs/ssr-dom-shim": "^1.2.1",
|
|
40
40
|
"@lit/react": "^1.0.7",
|
|
41
|
-
"@oslokommune/punkt-elements": "^13.
|
|
42
|
-
"angular-html-parser": "^6.0.2",
|
|
43
|
-
"html-format": "^1.1.7",
|
|
41
|
+
"@oslokommune/punkt-elements": "^13.3.1",
|
|
44
42
|
"prettier": "^3.3.3",
|
|
45
43
|
"react-element-to-jsx-string": "^15.0.0",
|
|
46
44
|
"react-hook-form": "^7.53.0",
|
|
47
|
-
"react-syntax-highlighter": "^15.
|
|
45
|
+
"react-syntax-highlighter": "^15.6.1"
|
|
48
46
|
},
|
|
49
47
|
"devDependencies": {
|
|
50
48
|
"@babel/plugin-proposal-private-property-in-object": "^7.18.6",
|
|
51
|
-
"@oslokommune/punkt-assets": "^13.
|
|
52
|
-
"@oslokommune/punkt-css": "^13.
|
|
49
|
+
"@oslokommune/punkt-assets": "^13.3.1",
|
|
50
|
+
"@oslokommune/punkt-css": "^13.3.1",
|
|
53
51
|
"@testing-library/jest-dom": "^6.5.0",
|
|
54
52
|
"@testing-library/react": "^16.0.1",
|
|
55
53
|
"@testing-library/user-event": "^14.5.2",
|
|
@@ -112,5 +110,5 @@
|
|
|
112
110
|
"url": "https://github.com/oslokommune/punkt/issues"
|
|
113
111
|
},
|
|
114
112
|
"license": "MIT",
|
|
115
|
-
"gitHead": "
|
|
113
|
+
"gitHead": "7b997c2cc58b531ccbf06cce080b0dd8556a46be"
|
|
116
114
|
}
|
package/src/components/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ export { PktDatepicker } from './datepicker/Datepicker'
|
|
|
12
12
|
export { PktFooter } from './footer/Footer'
|
|
13
13
|
export { PktFooterSimple } from './footerSimple/FooterSimple'
|
|
14
14
|
export { PktHeader } from './header/Header'
|
|
15
|
+
export { PktHeading } from './heading/Heading'
|
|
15
16
|
export { PktHelptext } from './helptext/Helptext'
|
|
16
17
|
export { PktIcon } from './icon/Icon'
|
|
17
18
|
export { PktInput } from './input/Input'
|
|
@@ -22,18 +23,19 @@ export { PktLoader } from './loader/Loader'
|
|
|
22
23
|
export { PktMessagebox } from './messagebox/Messagebox'
|
|
23
24
|
export { PktModal } from './modal/Modal'
|
|
24
25
|
export { PktPreview } from './preview/Preview'
|
|
26
|
+
export { PktPreviewWithJson } from './preview/PreviewWithJson'
|
|
25
27
|
export { PktProgressbar } from './progressbar/Progressbar'
|
|
26
28
|
export { PktRadioButton } from './radio/RadioButton'
|
|
27
29
|
export { PktSearchInput } from './searchinput/SearchInput'
|
|
28
30
|
export { PktSelect } from './select/Select'
|
|
29
|
-
export { PktStepper } from './stepper/Stepper'
|
|
30
31
|
export { PktStep } from './stepper/Step'
|
|
32
|
+
export { PktStepper } from './stepper/Stepper'
|
|
31
33
|
export { PktTable } from './table/Table'
|
|
34
|
+
export { PktTableBody } from './table/TableBody'
|
|
32
35
|
export { PktTableDataCell } from './table/TableDataCell'
|
|
33
|
-
export { PktTableHeaderCell } from './table/TableHeaderCell'
|
|
34
36
|
export { PktTableHeader } from './table/TableHeader'
|
|
37
|
+
export { PktTableHeaderCell } from './table/TableHeaderCell'
|
|
35
38
|
export { PktTableRow } from './table/TableRow'
|
|
36
|
-
export { PktTableBody } from './table/TableBody'
|
|
37
39
|
export { PktTabs } from './tabs/Tabs'
|
|
38
40
|
export { PktTag } from './tag/Tag'
|
|
39
41
|
export { PktTextarea } from './textarea/Textarea'
|
|
@@ -6,7 +6,7 @@ import { PktLinkCard as PktElLinkCard } from '@oslokommune/punkt-elements'
|
|
|
6
6
|
import type { PktElType, PktElConstructor } from '@/interfaces/IPktElements'
|
|
7
7
|
|
|
8
8
|
export interface IPktLinkCard extends PktElType {
|
|
9
|
-
skin?: 'normal' | 'blue' | 'beige' | 'green' | 'gray' | 'beige-outline' | 'gray-outline'
|
|
9
|
+
skin?: 'normal' | 'no-padding' | 'blue' | 'beige' | 'green' | 'gray' | 'beige-outline' | 'gray-outline'
|
|
10
10
|
title?: string
|
|
11
11
|
href?: string
|
|
12
12
|
iconName?: string
|
|
@@ -12,7 +12,8 @@ import { PktCheckbox } from '../checkbox/Checkbox'
|
|
|
12
12
|
import { PktTabs } from '../tabs/Tabs'
|
|
13
13
|
import { PktTag } from '../tag/Tag'
|
|
14
14
|
import { PktTextinput } from '../textinput/Textinput'
|
|
15
|
-
import { PktPreviewSpecs
|
|
15
|
+
import { PktPreviewSpecs } from './PreviewSpecs'
|
|
16
|
+
import type { ISpecObject } from './preview-types'
|
|
16
17
|
import { PktPreviewPropEditor } from './PreviewPropEditor'
|
|
17
18
|
|
|
18
19
|
SyntaxHighlighter.registerLanguage('jsx', jsx)
|
|
@@ -63,7 +64,8 @@ export const PktPreview: React.FC<PreviewProps> = ({ specs, children, fullWidth
|
|
|
63
64
|
const [htmlContent, setHtmlContent] = useState('')
|
|
64
65
|
const [jsxContent, setJsxContent] = useState('')
|
|
65
66
|
const [copied, setCopied] = useState('')
|
|
66
|
-
const [slotContent, setSlotContent] = useState('
|
|
67
|
+
const [slotContent, setSlotContent] = useState('Innhold')
|
|
68
|
+
|
|
67
69
|
const [tabs, setTabs] = useState([
|
|
68
70
|
{
|
|
69
71
|
text: 'Rediger',
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react'
|
|
2
|
+
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
|
|
3
|
+
import { a11yDark } from 'react-syntax-highlighter/dist/esm/styles/prism'
|
|
4
|
+
import * as prettier from 'prettier/standalone'
|
|
5
|
+
import * as parserHtml from 'prettier/parser-html'
|
|
6
|
+
import type { TPreviewTreeNode, TSpecs } from './preview-types'
|
|
7
|
+
import { PktButton } from '../button/Button'
|
|
8
|
+
import { PktTabs } from '../tabs/Tabs'
|
|
9
|
+
|
|
10
|
+
import { sanitizeCode, buildElementsCode, buildReactCode } from './preview-utils'
|
|
11
|
+
|
|
12
|
+
export const PktPreviewCode: React.FC<{
|
|
13
|
+
tree: TPreviewTreeNode
|
|
14
|
+
specs: TSpecs
|
|
15
|
+
html: string
|
|
16
|
+
}> = ({ tree, specs, html }) => {
|
|
17
|
+
const [tabs, setTabs] = useState([
|
|
18
|
+
{ text: 'Punkt React', icon: 'react', active: true },
|
|
19
|
+
{ text: specs[tree.spec || ''].isElement ? 'Punkt Elements' : 'HTML', icon: 'code', active: false },
|
|
20
|
+
])
|
|
21
|
+
const [copied, setCopied] = useState<'jsx' | 'html' | ''>('')
|
|
22
|
+
const [activeTab, setActiveTab] = useState(0)
|
|
23
|
+
|
|
24
|
+
const htmlCodeRaw = specs[tree.spec || ''].isElement
|
|
25
|
+
? (buildElementsCode(tree, specs) ?? '').replace(/^\s*\n|\n\s*$/g, '').trim()
|
|
26
|
+
: html.replace(/<\!--.*?-->/g, '')
|
|
27
|
+
const [htmlCode, setHtmlCode] = useState('') // Start as empty string
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (htmlCodeRaw) {
|
|
30
|
+
prettier
|
|
31
|
+
.format(htmlCodeRaw, {
|
|
32
|
+
parser: 'html',
|
|
33
|
+
plugins: [parserHtml as any],
|
|
34
|
+
singleAttributePerLine: true,
|
|
35
|
+
bracketSameLine: true,
|
|
36
|
+
})
|
|
37
|
+
.then((formatted) => setHtmlCode(formatted.trim()))
|
|
38
|
+
.catch(() => setHtmlCode(htmlCodeRaw)) // fallback if prettier fails
|
|
39
|
+
} else {
|
|
40
|
+
setHtmlCode('')
|
|
41
|
+
}
|
|
42
|
+
}, [htmlCodeRaw])
|
|
43
|
+
|
|
44
|
+
const jsxCodeRaw = (buildReactCode(tree, specs) ?? '').replace(/^\s*\n|\n\s*$/g, '').trim()
|
|
45
|
+
const [jsxCode, setJsxCode] = useState('') // Start as empty string
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (jsxCodeRaw) {
|
|
48
|
+
prettier
|
|
49
|
+
.format(jsxCodeRaw, {
|
|
50
|
+
parser: 'html',
|
|
51
|
+
plugins: [parserHtml as any],
|
|
52
|
+
singleAttributePerLine: true,
|
|
53
|
+
bracketSameLine: true,
|
|
54
|
+
})
|
|
55
|
+
.then((formatted) => setJsxCode(formatted.trim()))
|
|
56
|
+
.catch(() => setJsxCode(jsxCodeRaw)) // fallback if prettier fails
|
|
57
|
+
} else {
|
|
58
|
+
setJsxCode('')
|
|
59
|
+
}
|
|
60
|
+
}, [jsxCodeRaw])
|
|
61
|
+
|
|
62
|
+
const changeTab = (id: number) => {
|
|
63
|
+
setActiveTab(id)
|
|
64
|
+
setCopied('')
|
|
65
|
+
setTabs((prevTabs) =>
|
|
66
|
+
prevTabs.map((tab, index) => ({
|
|
67
|
+
...tab,
|
|
68
|
+
active: index === id,
|
|
69
|
+
})),
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const copyToClipboard = (type: 'jsx' | 'html', content: string) => {
|
|
74
|
+
navigator.clipboard.writeText(content).then(() => setCopied(type))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div className="pkt-preview__code-container" data-mode="dark">
|
|
79
|
+
<PktTabs tabs={tabs} onTabSelected={changeTab} />
|
|
80
|
+
<div className={`pkt-preview__code${activeTab === 0 ? '' : ' pkt-hide'}`}>
|
|
81
|
+
<SyntaxHighlighter language="jsx" style={a11yDark}>
|
|
82
|
+
{typeof jsxCode === 'string' && jsxCode.length > 0 ? sanitizeCode(jsxCode) : ''}
|
|
83
|
+
</SyntaxHighlighter>
|
|
84
|
+
|
|
85
|
+
<div className="pkt-preview__copy">
|
|
86
|
+
{copied === 'jsx' && <span className="pkt-preview__copied">Kode kopiert til utklippstavle</span>}
|
|
87
|
+
<PktButton
|
|
88
|
+
skin="tertiary"
|
|
89
|
+
variant="icon-only"
|
|
90
|
+
size="small"
|
|
91
|
+
iconName="copy"
|
|
92
|
+
onClick={() => copyToClipboard('jsx', jsxCode)}
|
|
93
|
+
>
|
|
94
|
+
Kopier kode
|
|
95
|
+
</PktButton>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
<div className={`pkt-preview__code${activeTab === 1 ? '' : ' pkt-hide'}`}>
|
|
99
|
+
<SyntaxHighlighter language="markup" style={a11yDark}>
|
|
100
|
+
{typeof htmlCode === 'string' && htmlCode.length > 0 ? sanitizeCode(htmlCode) : ''}
|
|
101
|
+
</SyntaxHighlighter>
|
|
102
|
+
|
|
103
|
+
<div className="pkt-preview__copy">
|
|
104
|
+
{copied === 'html' && <span className="pkt-preview__copied">Kode kopiert til utklippstavle</span>}
|
|
105
|
+
<PktButton
|
|
106
|
+
skin="tertiary"
|
|
107
|
+
variant="icon-only"
|
|
108
|
+
size="small"
|
|
109
|
+
iconName="copy"
|
|
110
|
+
onClick={() => copyToClipboard('html', htmlCode.trim())}
|
|
111
|
+
>
|
|
112
|
+
Kopier kode
|
|
113
|
+
</PktButton>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
@@ -3,20 +3,17 @@
|
|
|
3
3
|
import { FC } from 'react'
|
|
4
4
|
import { PktCheckbox } from '../checkbox/Checkbox'
|
|
5
5
|
import { PktDatepicker } from '../datepicker/Datepicker'
|
|
6
|
-
import { PktInputWrapper } from '../inputwrapper/InputWrapper'
|
|
7
|
-
import { PktSelect } from '../select/Select'
|
|
8
|
-
import { PktTextinput } from '../textinput/Textinput'
|
|
9
6
|
import icons from '@oslokommune/punkt-assets/dist/icons/icons.json'
|
|
10
7
|
import { PktButton } from '../button/Button'
|
|
8
|
+
import { PktAccordion } from '../accordion/Accordion'
|
|
9
|
+
import { PktAccordionItem } from '../accordion/AccordionItem'
|
|
10
|
+
import { PktTextinput } from '../textinput/Textinput'
|
|
11
|
+
import { IPropEditorProps } from './preview-types'
|
|
12
|
+
import { PktTag } from '../tag/Tag'
|
|
13
|
+
import { PktSelect } from '../select/Select'
|
|
14
|
+
import { PktTextarea } from '../textarea/Textarea'
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
propKey: string
|
|
14
|
-
spec: any
|
|
15
|
-
props: any
|
|
16
|
-
handleChange: (key: string, value: any, displayAsFalse?: boolean) => void
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export const PktPreviewPropEditor: FC<PropEditorProps> = ({ propKey, spec, props, handleChange }) => {
|
|
16
|
+
export const PktPreviewPropEditor: FC<IPropEditorProps> = ({ propKey, spec, props, hideKey, handleChange }) => {
|
|
20
17
|
const editorTypes = {
|
|
21
18
|
BOOLEAN: 'boolean',
|
|
22
19
|
ICON: 'icon',
|
|
@@ -24,59 +21,47 @@ export const PktPreviewPropEditor: FC<PropEditorProps> = ({ propKey, spec, props
|
|
|
24
21
|
OBJECT: 'object',
|
|
25
22
|
DATE: 'ISOdatestring',
|
|
26
23
|
NUMBER: 'number',
|
|
24
|
+
LONGSTRING: 'longstring',
|
|
27
25
|
STRING: 'string',
|
|
28
26
|
}
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
handleChange(propKey, { ...props[propKey], [key]: value })
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const handleArrayChange = (index: number, key: string, value: any) => {
|
|
35
|
-
const newArray = [...props[propKey]]
|
|
36
|
-
newArray[index] = { ...newArray[index], [key]: value }
|
|
37
|
-
handleChange(propKey, newArray)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const addObjectToArray = () => {
|
|
41
|
-
const newArray = [...props[propKey], {}]
|
|
42
|
-
handleChange(propKey, newArray)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const removeObjectFromArray = (index: number) => {
|
|
46
|
-
const newArray = props[propKey].filter((_: any, i: number) => i !== index)
|
|
47
|
-
handleChange(propKey, newArray)
|
|
48
|
-
}
|
|
49
|
-
|
|
28
|
+
// Boolean
|
|
50
29
|
if (spec.type === editorTypes.BOOLEAN) {
|
|
30
|
+
// Support both boolean and { value, displayAsFalse }
|
|
31
|
+
const raw = props[propKey]
|
|
32
|
+
const checked = typeof raw === 'object' && raw !== null && 'value' in raw ? raw.value : !!raw
|
|
51
33
|
return (
|
|
52
|
-
<
|
|
34
|
+
<PktCheckbox
|
|
35
|
+
id={propKey}
|
|
53
36
|
label={spec.name || propKey}
|
|
54
|
-
|
|
55
|
-
helptext={spec.description || null}
|
|
56
|
-
hasFieldset
|
|
37
|
+
tagText={hideKey ? undefined : propKey}
|
|
57
38
|
requiredTag={spec.required}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
39
|
+
type="checkbox"
|
|
40
|
+
checked={checked}
|
|
41
|
+
onChange={(e) => {
|
|
42
|
+
handleChange(
|
|
43
|
+
propKey,
|
|
44
|
+
spec.displayAsFalse ? { value: e.target.checked, displayAsFalse: true } : e.target.checked,
|
|
45
|
+
)
|
|
46
|
+
}}
|
|
47
|
+
labelPosition="right"
|
|
48
|
+
isSwitch
|
|
49
|
+
/>
|
|
69
50
|
)
|
|
70
51
|
}
|
|
71
52
|
|
|
53
|
+
// Icon (dropdown)
|
|
72
54
|
if (spec.type === editorTypes.ICON) {
|
|
55
|
+
const value = props[propKey] || ''
|
|
73
56
|
return (
|
|
74
57
|
<PktSelect
|
|
75
58
|
label={spec.name || propKey}
|
|
76
|
-
helptext={spec.description || null}
|
|
77
59
|
id={propKey}
|
|
78
|
-
|
|
79
|
-
|
|
60
|
+
tagText={hideKey ? undefined : propKey}
|
|
61
|
+
value={value}
|
|
62
|
+
onChange={(e) => {
|
|
63
|
+
handleChange(propKey, e.target.value)
|
|
64
|
+
}}
|
|
80
65
|
requiredTag={spec.required}
|
|
81
66
|
>
|
|
82
67
|
<option value=""></option>
|
|
@@ -89,31 +74,40 @@ export const PktPreviewPropEditor: FC<PropEditorProps> = ({ propKey, spec, props
|
|
|
89
74
|
)
|
|
90
75
|
}
|
|
91
76
|
|
|
77
|
+
// Date
|
|
92
78
|
if (spec.type === editorTypes.DATE) {
|
|
79
|
+
const value = props[propKey] || ''
|
|
93
80
|
return (
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
81
|
+
<div>
|
|
82
|
+
<PktDatepicker
|
|
83
|
+
id={propKey}
|
|
84
|
+
label={spec.name || propKey}
|
|
85
|
+
value={value}
|
|
86
|
+
multiple={!!(spec.variant === 'multiple')}
|
|
87
|
+
maxlength={999}
|
|
88
|
+
tagText={propKey}
|
|
89
|
+
onChange={(e) => {
|
|
90
|
+
handleChange(propKey, (e.target as HTMLInputElement).value)
|
|
91
|
+
}}
|
|
92
|
+
requiredTag={spec.required}
|
|
93
|
+
/>
|
|
94
|
+
</div>
|
|
104
95
|
)
|
|
105
96
|
}
|
|
106
97
|
|
|
98
|
+
// Enum (array of values)
|
|
107
99
|
if (Array.isArray(spec) || Array.isArray(spec.type) || Array.isArray(spec.values)) {
|
|
108
|
-
const arr = Array.isArray(spec) ? spec : spec.values
|
|
100
|
+
const arr = Array.isArray(spec) ? [...spec] : spec.values ? [...spec.values] : [...spec.type]
|
|
101
|
+
const value = props[propKey] || ''
|
|
109
102
|
return (
|
|
110
103
|
<PktSelect
|
|
111
104
|
label={spec.name || propKey}
|
|
112
|
-
helptext={spec.description || null}
|
|
113
105
|
id={propKey}
|
|
114
|
-
value={
|
|
115
|
-
onChange={(e) => handleChange(propKey, e.
|
|
106
|
+
value={value}
|
|
107
|
+
onChange={(e) => handleChange(propKey, e.currentTarget.value)}
|
|
108
|
+
required={spec.required}
|
|
116
109
|
requiredTag={spec.required}
|
|
110
|
+
tagText={hideKey ? undefined : propKey}
|
|
117
111
|
>
|
|
118
112
|
<option value=""></option>
|
|
119
113
|
{arr.map((option: string) => (
|
|
@@ -125,90 +119,148 @@ export const PktPreviewPropEditor: FC<PropEditorProps> = ({ propKey, spec, props
|
|
|
125
119
|
)
|
|
126
120
|
}
|
|
127
121
|
|
|
122
|
+
// Number
|
|
128
123
|
if (spec.type === editorTypes.NUMBER) {
|
|
124
|
+
const value = props[propKey] ?? ''
|
|
129
125
|
return (
|
|
130
126
|
<PktTextinput
|
|
131
|
-
id={propKey}
|
|
132
127
|
label={spec.name || propKey}
|
|
133
|
-
|
|
134
|
-
type="number"
|
|
135
|
-
value={props[propKey] || ''}
|
|
136
|
-
onChange={(e) => handleChange(propKey, parseFloat(e.currentTarget.value))}
|
|
128
|
+
id={propKey}
|
|
137
129
|
requiredTag={spec.required}
|
|
130
|
+
required={spec.required}
|
|
131
|
+
tagText={hideKey ? undefined : propKey}
|
|
132
|
+
type="number"
|
|
133
|
+
value={value}
|
|
134
|
+
onChange={(e) => {
|
|
135
|
+
handleChange(propKey, parseFloat(e.currentTarget.value))
|
|
136
|
+
}}
|
|
138
137
|
/>
|
|
139
138
|
)
|
|
140
139
|
}
|
|
141
140
|
|
|
141
|
+
// Object
|
|
142
142
|
if (spec.type === editorTypes.OBJECT) {
|
|
143
|
-
|
|
144
|
-
props[propKey] = {}
|
|
145
|
-
}
|
|
143
|
+
const value = props[propKey] ? { ...props[propKey] } : {}
|
|
146
144
|
return (
|
|
147
145
|
<div>
|
|
148
|
-
<
|
|
149
|
-
|
|
150
|
-
</
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
<fieldset>
|
|
154
|
-
<legend>{spec.name || propKey}</legend>
|
|
155
|
-
{Object.entries(spec.properties).map(([key, prop]) => (
|
|
146
|
+
<h2 className="pkt-inputwrapper__label">
|
|
147
|
+
{spec.name || propKey} <PktTag skin="gray">{propKey}</PktTag>
|
|
148
|
+
</h2>
|
|
149
|
+
<fieldset className="pkt-preview__object-editor">
|
|
150
|
+
{Object.entries(spec.properties).map(([key, childSpec]) => (
|
|
156
151
|
<PktPreviewPropEditor
|
|
157
152
|
key={key}
|
|
158
153
|
propKey={key}
|
|
159
|
-
props={
|
|
160
|
-
spec={
|
|
161
|
-
|
|
162
|
-
|
|
154
|
+
props={value}
|
|
155
|
+
spec={childSpec}
|
|
156
|
+
hideKey={hideKey}
|
|
157
|
+
handleChange={(childKey, childValue) => {
|
|
158
|
+
handleChange(propKey, { ...value, [childKey]: childValue })
|
|
159
|
+
}}
|
|
160
|
+
/>
|
|
163
161
|
))}
|
|
164
162
|
</fieldset>
|
|
165
163
|
</div>
|
|
166
164
|
)
|
|
167
165
|
}
|
|
168
166
|
|
|
167
|
+
// Array of objects
|
|
169
168
|
if (spec.type === editorTypes.ARRAY && spec.items.type === editorTypes.OBJECT) {
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
const value = Array.isArray(props[propKey]) ? [...props[propKey]] : []
|
|
170
|
+
const addObjectToArray = () => {
|
|
171
|
+
handleChange(propKey, [...value, {}])
|
|
172
|
+
}
|
|
173
|
+
const removeObjectFromArray = (index: number) => {
|
|
174
|
+
handleChange(
|
|
175
|
+
propKey,
|
|
176
|
+
value.filter((_, i) => i !== index),
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
const handleArrayChange = (index: number, childKey: string, childValue: any) => {
|
|
180
|
+
const newArr = value.map((item, i) => (i === index ? { ...item, [childKey]: childValue } : item))
|
|
181
|
+
handleChange(propKey, newArr)
|
|
172
182
|
}
|
|
173
183
|
return (
|
|
174
184
|
<div>
|
|
175
|
-
<
|
|
176
|
-
|
|
185
|
+
<h2 className="pkt-inputwrapper__label">
|
|
186
|
+
{spec.name || propKey} {hideKey ? '' : <PktTag skin="gray">{propKey}</PktTag>}
|
|
187
|
+
</h2>
|
|
188
|
+
<PktAccordion skin="outlined" compact>
|
|
189
|
+
{value.map((item: any, index: number) => (
|
|
190
|
+
<div key={index}>
|
|
191
|
+
<PktAccordionItem
|
|
192
|
+
id={`array-item-${index}`}
|
|
193
|
+
title={`${spec.items.name || propKey || `Item`} ${index + 1}`}
|
|
194
|
+
>
|
|
195
|
+
<div className="pkt-preview__array-item">
|
|
196
|
+
{Object.entries(spec.items.properties).map(([key, childSpec]) => (
|
|
197
|
+
<PktPreviewPropEditor
|
|
198
|
+
key={key}
|
|
199
|
+
propKey={key}
|
|
200
|
+
props={item}
|
|
201
|
+
spec={childSpec}
|
|
202
|
+
hideKey={hideKey}
|
|
203
|
+
handleChange={(childKey, childValue) => handleArrayChange(index, childKey, childValue)}
|
|
204
|
+
/>
|
|
205
|
+
))}
|
|
206
|
+
<div className="pkt-preview__remove-object">
|
|
207
|
+
<PktButton
|
|
208
|
+
onClick={() => {
|
|
209
|
+
confirm('Er du sikker på at du vil fjerne dette elementet?') && removeObjectFromArray(index)
|
|
210
|
+
}}
|
|
211
|
+
size="small"
|
|
212
|
+
skin="tertiary"
|
|
213
|
+
variant="icon-only"
|
|
214
|
+
iconName="trash-can"
|
|
215
|
+
>
|
|
216
|
+
Fjern denne
|
|
217
|
+
</PktButton>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
</PktAccordionItem>
|
|
221
|
+
</div>
|
|
222
|
+
))}
|
|
223
|
+
</PktAccordion>
|
|
224
|
+
<div className="pkt-preview__add-object">
|
|
225
|
+
<PktButton onClick={addObjectToArray} size="small" skin="tertiary" variant="icon-left" iconName="plus-circle">
|
|
226
|
+
Legg til {spec.items.name || propKey || 'element'}
|
|
227
|
+
</PktButton>
|
|
177
228
|
</div>
|
|
178
|
-
{spec.description && <p>{spec.description}</p>}
|
|
179
|
-
{props[propKey].map((item: any, index: number) => (
|
|
180
|
-
<div key={index}>
|
|
181
|
-
<fieldset>
|
|
182
|
-
<legend>Item {index + 1}</legend>
|
|
183
|
-
{Object.entries(spec.items.properties).map(([key, prop]) => (
|
|
184
|
-
<PktPreviewPropEditor
|
|
185
|
-
key={key}
|
|
186
|
-
propKey={key}
|
|
187
|
-
props={item}
|
|
188
|
-
spec={prop}
|
|
189
|
-
handleChange={(key, value) => handleArrayChange(index, key, value)}
|
|
190
|
-
></PktPreviewPropEditor>
|
|
191
|
-
))}
|
|
192
|
-
<PktButton onClick={() => removeObjectFromArray(index)}>Fjern denne</PktButton>
|
|
193
|
-
</fieldset>
|
|
194
|
-
</div>
|
|
195
|
-
))}
|
|
196
|
-
<PktButton onClick={addObjectToArray}>Legg til ny</PktButton>
|
|
197
229
|
</div>
|
|
198
230
|
)
|
|
199
231
|
}
|
|
200
232
|
|
|
233
|
+
if (spec.type === editorTypes.LONGSTRING) {
|
|
234
|
+
// Long string input
|
|
235
|
+
const value = props[propKey] || ''
|
|
236
|
+
return (
|
|
237
|
+
<PktTextarea
|
|
238
|
+
tagText={hideKey ? undefined : propKey}
|
|
239
|
+
id={propKey}
|
|
240
|
+
label={spec.name || propKey}
|
|
241
|
+
value={value}
|
|
242
|
+
onChange={(e) => {
|
|
243
|
+
handleChange(propKey, e.currentTarget.value)
|
|
244
|
+
}}
|
|
245
|
+
required={spec.required}
|
|
246
|
+
requiredTag={spec.required}
|
|
247
|
+
/>
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Default: string
|
|
252
|
+
const value = props[propKey] ?? ''
|
|
201
253
|
return (
|
|
202
254
|
<PktTextinput
|
|
203
|
-
id={propKey}
|
|
204
255
|
label={spec.name || propKey}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
onChange={(e) =>
|
|
209
|
-
handleChange(propKey, spec.type === 'number' ? parseFloat(e.currentTarget.value) : e.currentTarget.value)
|
|
210
|
-
}
|
|
256
|
+
tagText={hideKey ? undefined : propKey}
|
|
257
|
+
id={propKey}
|
|
258
|
+
required={spec.required}
|
|
211
259
|
requiredTag={spec.required}
|
|
260
|
+
value={value}
|
|
261
|
+
onChange={(e) => {
|
|
262
|
+
handleChange(propKey, e.currentTarget.value)
|
|
263
|
+
}}
|
|
212
264
|
/>
|
|
213
265
|
)
|
|
214
266
|
}
|
|
@@ -7,32 +7,10 @@ import { PktTableBody } from '../table/TableBody'
|
|
|
7
7
|
import { PktTableRow } from '../table/TableRow'
|
|
8
8
|
import { PktTableHeaderCell } from '../table/TableHeaderCell'
|
|
9
9
|
import { PktTableDataCell } from '../table/TableDataCell'
|
|
10
|
-
|
|
11
|
-
export interface ISpecObject {
|
|
12
|
-
name?: string
|
|
13
|
-
'css-class'?: string
|
|
14
|
-
'dark-mode'?: boolean
|
|
15
|
-
isElement?: boolean
|
|
16
|
-
props?: Record<string, PropObject | string[]>
|
|
17
|
-
events?: Record<string, PropObject>
|
|
18
|
-
slots?: Record<string, any>
|
|
19
|
-
default?: any
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
type PropObject = {
|
|
23
|
-
name?: string
|
|
24
|
-
description?: string
|
|
25
|
-
type?: string | string[]
|
|
26
|
-
variant?: string
|
|
27
|
-
converter?: string
|
|
28
|
-
reflect?: boolean
|
|
29
|
-
default?: any
|
|
30
|
-
previewDefault?: any
|
|
31
|
-
items?: PropObject
|
|
32
|
-
}
|
|
10
|
+
import { ISpecObject } from './preview-types'
|
|
33
11
|
|
|
34
12
|
export const PktPreviewSpecs: React.FC<{ specs: ISpecObject }> = ({ specs }) => {
|
|
35
|
-
const formatType = (type: string | string[]) => {
|
|
13
|
+
const formatType = (type: string | string[] | number | number[]) => {
|
|
36
14
|
if (Array.isArray(type)) {
|
|
37
15
|
return type.join(' \n')
|
|
38
16
|
}
|