Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
F
frama-c
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Releases
Container Registry
Model registry
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
pub
frama-c
Commits
1f785b8d
Commit
1f785b8d
authored
4 years ago
by
Loïc Correnson
Browse files
Options
Downloads
Patches
Plain Diff
[dome] text fields
parent
a454fcf3
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
ivette/src/dome/src/renderer/layout/form.tsx
+237
-19
237 additions, 19 deletions
ivette/src/dome/src/renderer/layout/form.tsx
with
237 additions
and
19 deletions
ivette/src/dome/src/renderer/layout/form.tsx
+
237
−
19
View file @
1f785b8d
...
@@ -49,7 +49,7 @@ export function validate<A>(
...
@@ -49,7 +49,7 @@ export function validate<A>(
export
function
isValid
(
err
:
Error
):
boolean
{
return
!
err
;
}
export
function
isValid
(
err
:
Error
):
boolean
{
return
!
err
;
}
type
ObjectError
=
{
[
key
:
string
]:
Error
}
type
ObjectError
=
{
[
key
:
string
]:
Error
}
;
function
isObjectError
(
err
:
Error
):
err
is
ObjectError
{
function
isObjectError
(
err
:
Error
):
err
is
ObjectError
{
return
typeof
err
===
'
object
'
&&
!
Array
.
isArray
(
err
);
return
typeof
err
===
'
object
'
&&
!
Array
.
isArray
(
err
);
...
@@ -86,14 +86,22 @@ export function useState<A>(
...
@@ -86,14 +86,22 @@ export function useState<A>(
const
[
value
,
setValue
]
=
React
.
useState
<
A
>
(
defaultValue
);
const
[
value
,
setValue
]
=
React
.
useState
<
A
>
(
defaultValue
);
const
[
error
,
setError
]
=
React
.
useState
<
Error
>
(
undefined
);
const
[
error
,
setError
]
=
React
.
useState
<
Error
>
(
undefined
);
const
setState
=
React
.
useCallback
((
newValue
:
A
,
newError
:
Error
)
=>
{
const
setState
=
React
.
useCallback
((
newValue
:
A
,
newError
:
Error
)
=>
{
const
localError
=
validate
(
v
alue
,
checker
)
||
newError
;
const
localError
=
validate
(
newV
alue
,
checker
)
||
newError
;
setValue
(
newValue
);
setValue
(
newValue
);
setError
(
localError
);
setError
(
localError
);
if
(
onChange
)
onChange
(
newValue
,
localError
);
if
(
onChange
)
onChange
(
newValue
,
localError
);
},
[
setValue
,
setError
,
onChange
]);
},
[
checker
,
setValue
,
setError
,
onChange
]);
return
[
value
,
error
,
setState
];
return
[
value
,
error
,
setState
];
}
}
export
function
useDefault
<
A
>
(
state
:
FieldState
<
A
|
undefined
>
,
defaultValue
:
A
,
):
FieldState
<
A
>
{
const
[
value
,
error
,
setState
]
=
state
;
return
[
value
??
defaultValue
,
error
,
setState
];
}
export
function
useChecker
<
A
>
(
export
function
useChecker
<
A
>
(
state
:
FieldState
<
A
>
,
state
:
FieldState
<
A
>
,
checker
?:
Checker
<
A
>
,
checker
?:
Checker
<
A
>
,
...
@@ -102,7 +110,7 @@ export function useChecker<A>(
...
@@ -102,7 +110,7 @@ export function useChecker<A>(
const
update
=
React
.
useCallback
((
newValue
:
A
,
newError
:
Error
)
=>
{
const
update
=
React
.
useCallback
((
newValue
:
A
,
newError
:
Error
)
=>
{
const
localError
=
validate
(
newValue
,
checker
)
||
newError
;
const
localError
=
validate
(
newValue
,
checker
)
||
newError
;
setState
(
newValue
,
localError
);
setState
(
newValue
,
localError
);
},
[
setState
]);
},
[
checker
,
setState
]);
return
[
value
,
error
,
update
];
return
[
value
,
error
,
update
];
}
}
...
@@ -110,7 +118,6 @@ export function useProperty<A, K extends keyof A>(
...
@@ -110,7 +118,6 @@ export function useProperty<A, K extends keyof A>(
state
:
FieldState
<
A
>
,
state
:
FieldState
<
A
>
,
property
:
K
,
property
:
K
,
checker
?:
Checker
<
A
[
K
]
>
,
checker
?:
Checker
<
A
[
K
]
>
,
onError
?:
string
,
):
FieldState
<
A
[
K
]
>
{
):
FieldState
<
A
[
K
]
>
{
const
[
value
,
error
,
setState
]
=
state
;
const
[
value
,
error
,
setState
]
=
state
;
const
update
=
React
.
useCallback
((
newProp
:
A
[
K
],
newError
:
Error
)
=>
{
const
update
=
React
.
useCallback
((
newProp
:
A
[
K
],
newError
:
Error
)
=>
{
...
@@ -119,7 +126,7 @@ export function useProperty<A, K extends keyof A>(
...
@@ -119,7 +126,7 @@ export function useProperty<A, K extends keyof A>(
const
propError
=
validate
(
newProp
,
checker
)
||
newError
;
const
propError
=
validate
(
newProp
,
checker
)
||
newError
;
const
localError
=
{
...
objError
,
[
property
]:
propError
};
const
localError
=
{
...
objError
,
[
property
]:
propError
};
setState
(
newValue
,
isValidObject
(
localError
)
?
undefined
:
localError
);
setState
(
newValue
,
isValidObject
(
localError
)
?
undefined
:
localError
);
},
[
value
,
error
,
setState
,
property
,
checker
,
onError
]);
},
[
value
,
error
,
setState
,
property
,
checker
]);
return
[
value
[
property
],
error
,
update
];
return
[
value
[
property
],
error
,
update
];
}
}
...
@@ -128,12 +135,12 @@ export function useLatency<A>(
...
@@ -128,12 +135,12 @@ export function useLatency<A>(
latency
?:
number
,
latency
?:
number
,
):
FieldState
<
A
>
{
):
FieldState
<
A
>
{
const
[
initValue
,
initError
,
setState
]
=
state
;
const
[
initValue
,
initError
,
setState
]
=
state
;
const
period
=
Math
.
max
(
latency
??
0
,
0
)
;
const
period
=
latency
??
0
;
const
[
value
,
setValue
]
=
React
.
useState
(
initValue
);
const
[
value
,
setValue
]
=
React
.
useState
(
initValue
);
const
[
error
,
setError
]
=
React
.
useState
(
initError
);
const
[
error
,
setError
]
=
React
.
useState
(
initError
);
const
propagate
=
React
.
use
Callback
(
const
propagate
=
React
.
use
Memo
(
debounce
(
setState
,
period
),
()
=>
(
period
>
0
?
debounce
(
setState
,
period
)
:
setState
)
,
[
latency
,
setState
],
[
period
,
setState
],
);
);
const
update
=
React
.
useCallback
((
newValue
,
newError
)
=>
{
const
update
=
React
.
useCallback
((
newValue
,
newError
)
=>
{
setValue
(
newValue
);
setValue
(
newValue
);
...
@@ -147,7 +154,6 @@ export function useIndex<A>(
...
@@ -147,7 +154,6 @@ export function useIndex<A>(
state
:
FieldState
<
A
[]
>
,
state
:
FieldState
<
A
[]
>
,
index
:
number
,
index
:
number
,
checker
?:
Checker
<
A
>
,
checker
?:
Checker
<
A
>
,
onError
?:
string
,
):
FieldState
<
A
>
{
):
FieldState
<
A
>
{
const
[
array
,
error
,
setState
]
=
state
;
const
[
array
,
error
,
setState
]
=
state
;
const
update
=
React
.
useCallback
((
newValue
:
A
,
newError
:
Error
)
=>
{
const
update
=
React
.
useCallback
((
newValue
:
A
,
newError
:
Error
)
=>
{
...
@@ -157,7 +163,7 @@ export function useIndex<A>(
...
@@ -157,7 +163,7 @@ export function useIndex<A>(
const
valueError
=
validate
(
newValue
,
checker
)
||
newError
;
const
valueError
=
validate
(
newValue
,
checker
)
||
newError
;
localError
[
index
]
=
valueError
;
localError
[
index
]
=
valueError
;
setState
(
newArray
,
isValidArray
(
localError
)
?
undefined
:
localError
);
setState
(
newArray
,
isValidArray
(
localError
)
?
undefined
:
localError
);
},
[
array
,
error
,
setState
,
index
,
checker
,
onError
]);
},
[
array
,
error
,
setState
,
index
,
checker
]);
const
itemError
=
isArrayError
(
error
)
?
error
[
index
]
:
undefined
;
const
itemError
=
isArrayError
(
error
)
?
error
[
index
]
:
undefined
;
return
[
array
[
index
],
itemError
,
update
];
return
[
array
[
index
],
itemError
,
update
];
}
}
...
@@ -284,9 +290,7 @@ export function Warning(props: WarningProps) {
...
@@ -284,9 +290,7 @@ export function Warning(props: WarningProps) {
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/**
/**
Layout its contents inside a full-width block.
Layout its contents inside a full-width container.
The children are _not_ supposed to contain `<Field />` like elements,
only custom controls that fits a full-width containter.
@category Form Containers
@category Form Containers
*/
*/
export
function
Block
(
props
:
FilterProps
&
Children
)
{
export
function
Block
(
props
:
FilterProps
&
Children
)
{
...
@@ -304,6 +308,7 @@ export function Block(props: FilterProps & Children) {
...
@@ -304,6 +308,7 @@ export function Block(props: FilterProps & Children) {
// --- Section Container
// --- Section Container
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
/** @category Form Fields */
export
interface
SectionProps
extends
FilterProps
,
Children
{
export
interface
SectionProps
extends
FilterProps
,
Children
{
/** Section name. */
/** Section name. */
label
:
string
;
label
:
string
;
...
@@ -319,7 +324,7 @@ export interface SectionProps extends FilterProps, Children {
...
@@ -319,7 +324,7 @@ export interface SectionProps extends FilterProps, Children {
unfold
?:
boolean
;
unfold
?:
boolean
;
}
}
/**
Form Section.
*/
/**
@category Form Fields
*/
export
function
Section
(
props
:
SectionProps
)
{
export
function
Section
(
props
:
SectionProps
)
{
const
{
label
,
title
,
children
,
warning
,
error
,
...
filter
}
=
props
;
const
{
label
,
title
,
children
,
warning
,
error
,
...
filter
}
=
props
;
const
{
disabled
,
hidden
}
=
useContext
(
filter
);
const
{
disabled
,
hidden
}
=
useContext
(
filter
);
...
@@ -352,10 +357,11 @@ export function Section(props: SectionProps) {
...
@@ -352,10 +357,11 @@ export function Section(props: SectionProps) {
}
}
/* --------------------------------------------------------------------------*/
/* --------------------------------------------------------------------------*/
/* ---
Value Filter
--- */
/* ---
Generic Field
--- */
/* --------------------------------------------------------------------------*/
/* --------------------------------------------------------------------------*/
export
interface
FieldProps
extends
FilterProps
,
Children
{
/** @category Form Fields */
export
interface
GenericFieldProps
extends
FilterProps
,
Children
{
/** Field label. */
/** Field label. */
label
:
string
;
label
:
string
;
/** Field tooltip text. */
/** Field tooltip text. */
...
@@ -364,10 +370,15 @@ export interface FieldProps extends FilterProps, Children {
...
@@ -364,10 +370,15 @@ export interface FieldProps extends FilterProps, Children {
offset
?:
number
;
offset
?:
number
;
/** Html tag `<input />` element. */
/** Html tag `<input />` element. */
htmlFor
?:
string
;
htmlFor
?:
string
;
/** Warning message (in case of error). */
onError
?:
string
;
/** Error (if any). */
error
?:
Error
;
}
}
let
FIELDID
=
0
;
let
FIELDID
=
0
;
/** Generates a unique, stable identifier. */
export
function
useHtmlFor
()
{
export
function
useHtmlFor
()
{
return
React
.
useMemo
(()
=>
`dome-field
${
FIELDID
++
}
`
,
[]);
return
React
.
useMemo
(()
=>
`dome-field
${
FIELDID
++
}
`
,
[]);
}
}
...
@@ -375,8 +386,9 @@ export function useHtmlFor() {
...
@@ -375,8 +386,9 @@ export function useHtmlFor() {
/**
/**
Generic Field.
Generic Field.
Layout its content in a top-left aligned box on the right of the label.
Layout its content in a top-left aligned box on the right of the label.
@category Form Fields
*/
*/
export
function
Field
(
props
:
FieldProps
)
{
export
function
Field
(
props
:
Generic
FieldProps
)
{
const
{
hidden
,
disabled
}
=
useContext
(
props
);
const
{
hidden
,
disabled
}
=
useContext
(
props
);
if
(
hidden
)
return
null
;
if
(
hidden
)
return
null
;
...
@@ -393,6 +405,12 @@ export function Field(props: FieldProps) {
...
@@ -393,6 +405,12 @@ export function Field(props: FieldProps) {
disabled
&&
'
dome-disabled
'
,
disabled
&&
'
dome-disabled
'
,
);
);
const
{
onError
,
error
}
=
props
;
const
WARNING
=
error
?
(
<
Warning
offset
=
{
offset
}
warning
=
{
onError
}
error
=
{
error
}
/>
)
:
null
;
return
(
return
(
<>
<>
<
label
<
label
...
@@ -405,10 +423,210 @@ export function Field(props: FieldProps) {
...
@@ -405,10 +423,210 @@ export function Field(props: FieldProps) {
</
label
>
</
label
>
<
div
className
=
{
cssField
}
>
<
div
className
=
{
cssField
}
>
{
children
}
{
children
}
{
WARNING
}
</
div
>
</
div
>
</>
</>
);
);
}
}
/* --------------------------------------------------------------------------*/
/* --- Input Fields ---*/
/* --------------------------------------------------------------------------*/
/** @category Form Fields */
export
interface
FieldProps
<
A
>
extends
FilterProps
{
/** Field label. */
label
:
string
;
/** Field tooltip text. */
title
?:
string
;
/** Field state. */
state
:
FieldState
<
A
>
;
/** Checker. */
checker
?:
Checker
<
A
>
;
/** Alternative error message (in case of error). */
onError
?:
string
;
}
type
InputEvent
=
{
target
:
{
value
:
string
}
};
type
InputState
=
[
string
,
Error
,
(
evt
:
InputEvent
)
=>
void
];
function
useTextInputField
(
props
:
FieldTextProps
,
defaultLatency
:
number
,
):
InputState
{
const
checked
=
useChecker
(
props
.
state
,
props
.
checker
);
const
period
=
props
.
latency
??
defaultLatency
;
const
[
value
,
error
,
setState
]
=
useLatency
(
checked
,
period
);
const
onChange
=
(
evt
:
InputEvent
)
=>
{
setState
(
evt
.
target
.
value
,
undefined
);
};
return
[
value
||
''
,
error
,
onChange
];
}
/* --------------------------------------------------------------------------*/
/* --- Text Fields ---*/
/* --------------------------------------------------------------------------*/
/** @category Form Fields */
export
interface
FieldTextProps
extends
FieldProps
<
string
|
undefined
>
{
placeholder
?:
string
;
className
?:
string
;
style
?:
React
.
CSSProperties
;
latency
?:
number
;
}
/**
Text Field.
@category Form Fields
*/
export
const
FieldText
=
(
props
:
FieldTextProps
)
=>
{
const
{
disabled
}
=
useContext
(
props
);
const
id
=
useHtmlFor
();
const
css
=
Utils
.
classes
(
'
dome-xForm-text-field
'
,
props
.
className
);
const
[
value
,
error
,
onChange
]
=
useTextInputField
(
props
,
600
);
return
(
<
Field
{
...
props
}
offset
=
{
4
}
htmlFor
=
{
id
}
error
=
{
error
}
>
<
input
id
=
{
id
}
type
=
"text"
value
=
{
value
}
className
=
{
css
}
style
=
{
props
.
style
}
disabled
=
{
disabled
}
placeholder
=
{
props
.
placeholder
}
onChange
=
{
onChange
}
/>
</
Field
>
);
};
/**
Monospaced Text Field.
@category Form Fields
*/
export
const
FieldCode
=
(
props
:
FieldTextProps
)
=>
{
const
{
disabled
}
=
useContext
(
props
);
const
id
=
useHtmlFor
();
const
[
value
,
error
,
onChange
]
=
useTextInputField
(
props
,
600
);
const
css
=
Utils
.
classes
(
'
dome-xForm-text-field
'
,
'
dome-text-code
'
,
props
.
className
,
);
return
(
<
Field
{
...
props
}
offset
=
{
4
}
htmlFor
=
{
id
}
error
=
{
error
}
>
<
input
id
=
{
id
}
type
=
"text"
value
=
{
value
}
className
=
{
css
}
style
=
{
props
.
style
}
disabled
=
{
disabled
}
placeholder
=
{
props
.
placeholder
}
onChange
=
{
onChange
}
/>
</
Field
>
);
};
/* --------------------------------------------------------------------------*/
/* --- Text Area Fields ---*/
/* --------------------------------------------------------------------------*/
/** @category Form Fields */
export
interface
FieldTextAreaProps
extends
FieldTextProps
{
/** Number of columns (default 35, min 5). */
cols
?:
number
;
/** Number of rows (default 5, min 2). */
rows
?:
number
;
}
/**
Text Field Area.
@category Form Fields
*/
export
const
FieldTextArea
=
(
props
:
FieldTextAreaProps
)
=>
{
const
{
disabled
}
=
useContext
(
props
);
const
id
=
useHtmlFor
();
const
[
value
,
error
,
onChange
]
=
useTextInputField
(
props
,
900
);
const
cols
=
Math
.
max
(
5
,
props
.
cols
??
35
);
const
rows
=
Math
.
max
(
2
,
props
.
rows
??
5
);
const
css
=
Utils
.
classes
(
'
dome-xForm-textarea-field
'
,
props
.
className
,
);
return
(
<
Field
{
...
props
}
offset
=
{
4
}
htmlFor
=
{
id
}
error
=
{
error
}
>
<
textarea
id
=
{
id
}
wrap
=
"hard"
spellCheck
value
=
{
value
}
cols
=
{
cols
}
rows
=
{
rows
-
1
}
className
=
{
css
}
style
=
{
props
.
style
}
disabled
=
{
disabled
}
placeholder
=
{
props
.
placeholder
}
onChange
=
{
onChange
}
/>
</
Field
>
);
};
/**
Monospaced Text Field Area.
@category Form Fields
*/
export
const
FieldCodeArea
=
(
props
:
FieldTextAreaProps
)
=>
{
const
{
disabled
}
=
useContext
(
props
);
const
id
=
useHtmlFor
();
const
[
value
,
error
,
onChange
]
=
useTextInputField
(
props
,
900
);
const
cols
=
Math
.
max
(
5
,
props
.
cols
??
35
);
const
rows
=
Math
.
max
(
2
,
props
.
rows
??
5
);
const
css
=
Utils
.
classes
(
'
dome-xForm-textarea-field
'
,
'
dome-text-code
'
,
props
.
className
,
);
return
(
<
Field
{
...
props
}
offset
=
{
4
}
htmlFor
=
{
id
}
error
=
{
error
}
>
<
textarea
id
=
{
id
}
wrap
=
"off"
spellCheck
=
{
false
}
value
=
{
value
}
cols
=
{
cols
}
rows
=
{
rows
}
className
=
{
css
}
style
=
{
props
.
style
}
disabled
=
{
disabled
}
placeholder
=
{
props
.
placeholder
}
onChange
=
{
onChange
}
/>
</
Field
>
);
};
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment