@furystack/shades-common-components 3.5.3 → 3.6.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/dist/components/app-bar-link.js +8 -10
- package/dist/components/app-bar-link.js.map +1 -1
- package/dist/components/app-bar.js +3 -7
- package/dist/components/app-bar.js.map +1 -1
- package/dist/components/button.js +37 -27
- package/dist/components/button.js.map +1 -1
- package/dist/components/command-palette/command-palette-suggestion-list.js +1 -1
- package/dist/components/command-palette/command-palette-suggestion-list.js.map +1 -1
- package/dist/components/data-grid/body.js +3 -4
- package/dist/components/data-grid/body.js.map +1 -1
- package/dist/components/data-grid/data-grid-row.js +1 -1
- package/dist/components/data-grid/data-grid-row.js.map +1 -1
- package/dist/components/data-grid/data-grid.js +4 -15
- package/dist/components/data-grid/data-grid.js.map +1 -1
- package/dist/components/data-grid/footer.js +3 -10
- package/dist/components/data-grid/footer.js.map +1 -1
- package/dist/components/grid.js +1 -10
- package/dist/components/grid.js.map +1 -1
- package/dist/components/inputs/input.js +10 -14
- package/dist/components/inputs/input.js.map +1 -1
- package/dist/components/inputs/text-area.js +3 -7
- package/dist/components/inputs/text-area.js.map +1 -1
- package/dist/components/loader.js +0 -10
- package/dist/components/loader.js.map +1 -1
- package/dist/components/noty-list.js +3 -3
- package/dist/components/noty-list.js.map +1 -1
- package/dist/components/paper.js +2 -12
- package/dist/components/paper.js.map +1 -1
- package/dist/components/tabs.js +2 -10
- package/dist/components/tabs.js.map +1 -1
- package/dist/services/collection-service.js +16 -8
- package/dist/services/collection-service.js.map +1 -1
- package/dist/services/default-variable-theme.js +80 -0
- package/dist/services/default-variable-theme.js.map +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/index.js.map +1 -1
- package/dist/services/theme-provider-service.js +9 -7
- package/dist/services/theme-provider-service.js.map +1 -1
- package/package.json +7 -7
- package/src/components/app-bar-link.tsx +9 -12
- package/src/components/app-bar.tsx +3 -6
- package/src/components/button.tsx +44 -31
- package/src/components/command-palette/command-palette-suggestion-list.tsx +1 -1
- package/src/components/data-grid/body.tsx +6 -3
- package/src/components/data-grid/data-grid-row.tsx +6 -1
- package/src/components/data-grid/data-grid.tsx +16 -14
- package/src/components/data-grid/footer.tsx +3 -11
- package/src/components/grid.tsx +1 -10
- package/src/components/inputs/input.tsx +11 -16
- package/src/components/inputs/text-area.tsx +3 -9
- package/src/components/loader.tsx +0 -9
- package/src/components/noty-list.tsx +3 -3
- package/src/components/paper.tsx +3 -11
- package/src/components/tabs.tsx +2 -10
- package/src/services/collection-service.ts +54 -11
- package/src/services/default-variable-theme.ts +83 -0
- package/src/services/index.ts +1 -0
- package/src/services/theme-provider-service.ts +11 -8
- package/tsconfig.tsbuildinfo +1 -1
- package/types/components/app-bar-link.d.ts.map +1 -1
- package/types/components/app-bar.d.ts.map +1 -1
- package/types/components/button.d.ts.map +1 -1
- package/types/components/data-grid/body.d.ts +3 -1
- package/types/components/data-grid/body.d.ts.map +1 -1
- package/types/components/data-grid/data-grid-row.d.ts +1 -0
- package/types/components/data-grid/data-grid-row.d.ts.map +1 -1
- package/types/components/data-grid/data-grid.d.ts +8 -0
- package/types/components/data-grid/data-grid.d.ts.map +1 -1
- package/types/components/data-grid/footer.d.ts.map +1 -1
- package/types/components/grid.d.ts.map +1 -1
- package/types/components/inputs/input.d.ts +1 -2
- package/types/components/inputs/input.d.ts.map +1 -1
- package/types/components/inputs/text-area.d.ts +0 -2
- package/types/components/inputs/text-area.d.ts.map +1 -1
- package/types/components/loader.d.ts.map +1 -1
- package/types/components/paper.d.ts.map +1 -1
- package/types/components/tabs.d.ts.map +1 -1
- package/types/services/collection-service.d.ts +32 -2
- package/types/services/collection-service.d.ts.map +1 -1
- package/types/services/default-variable-theme.d.ts +7 -0
- package/types/services/default-variable-theme.d.ts.map +1 -0
- package/types/services/index.d.ts +1 -0
- package/types/services/index.d.ts.map +1 -1
- package/types/services/theme-provider-service.d.ts +2 -3
- package/types/services/theme-provider-service.d.ts.map +1 -1
|
@@ -8,7 +8,7 @@ import { DataGridRow } from './data-grid-row'
|
|
|
8
8
|
export interface DataGridBodyProps<T> {
|
|
9
9
|
service: CollectionService<T>
|
|
10
10
|
onRowClick?: (row: T, ev: MouseEvent) => void
|
|
11
|
-
onRowDoubleClick?: (entry: T) => void
|
|
11
|
+
onRowDoubleClick?: (entry: T, ev: MouseEvent) => void
|
|
12
12
|
columns: Array<keyof T>
|
|
13
13
|
rowComponents?: DataRowCells<T>
|
|
14
14
|
style?: Partial<CSSStyleDeclaration>
|
|
@@ -16,6 +16,8 @@ export interface DataGridBodyProps<T> {
|
|
|
16
16
|
unfocusedRowStyle?: Partial<CSSStyleDeclaration>
|
|
17
17
|
selectedRowStyle?: Partial<CSSStyleDeclaration>
|
|
18
18
|
unselectedRowStyle?: Partial<CSSStyleDeclaration>
|
|
19
|
+
emptyComponent?: JSX.Element
|
|
20
|
+
loaderComponent?: JSX.Element
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
export interface DataGridBodyState<T> {
|
|
@@ -44,13 +46,13 @@ export const DataGridBody: <T>(props: DataGridBodyProps<T>, children: ChildrenLi
|
|
|
44
46
|
return (
|
|
45
47
|
<div style={{ display: 'flex', height: '100%', justifyContent: 'center', alignItems: 'center', width: '100%' }}>
|
|
46
48
|
{/* TODO: Skeleton */}
|
|
47
|
-
<Loader style={{ height: '128px', width: '128px' }} />
|
|
49
|
+
{props.loaderComponent || <Loader style={{ height: '128px', width: '128px' }} />}
|
|
48
50
|
</div>
|
|
49
51
|
)
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
if (!state.data?.length) {
|
|
53
|
-
return <div> - No Data - </div>
|
|
55
|
+
return props.emptyComponent || <div> - No Data - </div>
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
return (
|
|
@@ -62,6 +64,7 @@ export const DataGridBody: <T>(props: DataGridBodyProps<T>, children: ChildrenLi
|
|
|
62
64
|
service={props.service}
|
|
63
65
|
rowComponents={props.rowComponents}
|
|
64
66
|
onRowClick={props.onRowClick}
|
|
67
|
+
onRowDoubleClick={props.onRowDoubleClick}
|
|
65
68
|
focusedRowStyle={props.focusedRowStyle}
|
|
66
69
|
unfocusedRowStyle={props.unfocusedRowStyle}
|
|
67
70
|
selectedRowStyle={props.selectedRowStyle}
|
|
@@ -9,6 +9,7 @@ export interface DataGridRowProps<T> {
|
|
|
9
9
|
service: CollectionService<T>
|
|
10
10
|
rowComponents?: DataRowCells<T>
|
|
11
11
|
onRowClick?: (row: T, event: MouseEvent) => void
|
|
12
|
+
onRowDoubleClick?: (row: T, event: MouseEvent) => void
|
|
12
13
|
focusedRowStyle?: Partial<CSSStyleDeclaration>
|
|
13
14
|
selectedRowStyle?: Partial<CSSStyleDeclaration>
|
|
14
15
|
unfocusedRowStyle?: Partial<CSSStyleDeclaration>
|
|
@@ -100,7 +101,11 @@ export const DataGridRow: <T>(props: DataGridRowProps<T>, children: ChildrenList
|
|
|
100
101
|
return (
|
|
101
102
|
<>
|
|
102
103
|
{columns.map((column) => (
|
|
103
|
-
<td
|
|
104
|
+
<td
|
|
105
|
+
style={{ padding: '0.5em' }}
|
|
106
|
+
onclick={(ev) => props.onRowClick?.(entry, ev)}
|
|
107
|
+
ondblclick={(ev) => props.onRowDoubleClick?.(entry, ev)}
|
|
108
|
+
>
|
|
104
109
|
{rowComponents?.[column]?.(entry, state) || rowComponents?.default?.(entry, state) || (
|
|
105
110
|
<span>{entry[column]}</span>
|
|
106
111
|
)}
|
|
@@ -56,23 +56,24 @@ export interface DataGridProps<T> {
|
|
|
56
56
|
* Optional style to attach to grid rows when the row is not selected
|
|
57
57
|
*/
|
|
58
58
|
unselectedRowStyle?: Partial<CSSStyleDeclaration>
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* An optional component to show if there are no rows to display
|
|
62
|
+
*/
|
|
63
|
+
emptyComponent?: JSX.Element
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* An optional component to show while the data is loading
|
|
67
|
+
*/
|
|
68
|
+
loaderComponent?: JSX.Element
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
export const DataGrid: <T>(props: DataGridProps<T>, children: ChildrenList) => JSX.Element<any, any> = Shade<
|
|
62
72
|
DataGridProps<any>
|
|
63
73
|
>({
|
|
64
74
|
shadowDomName: 'shade-data-grid',
|
|
65
|
-
resources: ({
|
|
66
|
-
const tp = injector.getInstance(ThemeProviderService)
|
|
75
|
+
resources: ({ element, props }) => {
|
|
67
76
|
return [
|
|
68
|
-
tp.theme.subscribe((t) => {
|
|
69
|
-
const headers = element.querySelectorAll('th')
|
|
70
|
-
const { r, g, b } = tp.getRgbFromColorString(t.background.paper)
|
|
71
|
-
headers.forEach((header) => {
|
|
72
|
-
header.style.color = t.text.secondary
|
|
73
|
-
header.style.backgroundColor = `rgba(${r}, ${g}, ${b}, 0.3)`
|
|
74
|
-
})
|
|
75
|
-
}),
|
|
76
77
|
new ClickAwayService(element, () => {
|
|
77
78
|
props.service.hasFocus.setValue(false)
|
|
78
79
|
}),
|
|
@@ -85,12 +86,10 @@ export const DataGrid: <T>(props: DataGridProps<T>, children: ChildrenList) => J
|
|
|
85
86
|
},
|
|
86
87
|
render: ({ props, injector }) => {
|
|
87
88
|
const tp = injector.getInstance(ThemeProviderService)
|
|
88
|
-
const theme = tp
|
|
89
|
+
const { theme } = tp
|
|
89
90
|
|
|
90
|
-
const { r, g, b } = tp.getRgbFromColorString(theme.background.paper)
|
|
91
91
|
const headerStyle: Partial<CSSStyleDeclaration> = {
|
|
92
|
-
|
|
93
|
-
backdropFilter: 'blur(10px)',
|
|
92
|
+
backdropFilter: 'blur(12px)',
|
|
94
93
|
padding: '12px 0',
|
|
95
94
|
color: theme.text.secondary,
|
|
96
95
|
alignItems: 'center',
|
|
@@ -137,11 +136,14 @@ export const DataGrid: <T>(props: DataGridProps<T>, children: ChildrenList) => J
|
|
|
137
136
|
service={props.service}
|
|
138
137
|
rowComponents={props.rowComponents}
|
|
139
138
|
onRowClick={(entry, ev) => props.service.handleRowClick(entry, ev)}
|
|
139
|
+
onRowDoubleClick={(entry) => props.service.handleRowDoubleClick(entry)}
|
|
140
140
|
style={props.styles?.cell}
|
|
141
141
|
focusedRowStyle={props.focusedRowStyle}
|
|
142
142
|
selectedRowStyle={props.selectedRowStyle}
|
|
143
143
|
unfocusedRowStyle={props.unfocusedRowStyle}
|
|
144
144
|
unselectedRowStyle={props.unselectedRowStyle}
|
|
145
|
+
emptyComponent={props.emptyComponent}
|
|
146
|
+
loaderComponent={props.loaderComponent}
|
|
145
147
|
/>
|
|
146
148
|
</table>
|
|
147
149
|
<DataGridFooter service={props.service} />
|
|
@@ -9,16 +9,8 @@ export const DataGridFooter = Shade<{ service: CollectionService<any> }, { data:
|
|
|
9
9
|
getInitialState: ({ props }) => ({
|
|
10
10
|
data: props.service.data.getValue(),
|
|
11
11
|
}),
|
|
12
|
-
constructed: ({ props, updateState
|
|
13
|
-
const disposables = [
|
|
14
|
-
props.service.data.subscribe((data) => updateState({ data })),
|
|
15
|
-
|
|
16
|
-
injector.getInstance(ThemeProviderService).theme.subscribe((t) => {
|
|
17
|
-
const el = element.querySelector('div') as HTMLDivElement
|
|
18
|
-
el.style.color = t.text.secondary
|
|
19
|
-
el.style.background = t.background.paper
|
|
20
|
-
}),
|
|
21
|
-
]
|
|
12
|
+
constructed: ({ props, updateState }) => {
|
|
13
|
+
const disposables = [props.service.data.subscribe((data) => updateState({ data }))]
|
|
22
14
|
|
|
23
15
|
return () => disposables.forEach((d) => d.dispose())
|
|
24
16
|
},
|
|
@@ -27,7 +19,7 @@ export const DataGridFooter = Shade<{ service: CollectionService<any> }, { data:
|
|
|
27
19
|
const currentQuerySettings = props.service.querySettings.getValue()
|
|
28
20
|
const currentPage = Math.ceil(currentQuerySettings.skip || 0) / (currentQuerySettings.top || 1)
|
|
29
21
|
const currentEntriesPerPage = currentQuerySettings.top || Infinity
|
|
30
|
-
const theme = injector.getInstance(ThemeProviderService)
|
|
22
|
+
const { theme } = injector.getInstance(ThemeProviderService)
|
|
31
23
|
|
|
32
24
|
return (
|
|
33
25
|
<div
|
package/src/components/grid.tsx
CHANGED
|
@@ -25,17 +25,8 @@ export type RowCells<T> = {
|
|
|
25
25
|
|
|
26
26
|
export const Grid: <T>(props: GridProps<T>, children: ChildrenList) => JSX.Element<any, any> = Shade({
|
|
27
27
|
shadowDomName: 'shade-grid',
|
|
28
|
-
resources: ({ injector, element }) => [
|
|
29
|
-
injector.getInstance(ThemeProviderService).theme.subscribe((t) => {
|
|
30
|
-
const headers = element.querySelectorAll('th')
|
|
31
|
-
headers.forEach((header) => {
|
|
32
|
-
header.style.color = t.text.secondary
|
|
33
|
-
header.style.background = t.background.paper
|
|
34
|
-
})
|
|
35
|
-
}),
|
|
36
|
-
],
|
|
37
28
|
render: ({ props, injector }) => {
|
|
38
|
-
const theme = injector.getInstance(ThemeProviderService)
|
|
29
|
+
const { theme } = injector.getInstance(ThemeProviderService)
|
|
39
30
|
const headerStyle: Partial<CSSStyleDeclaration> = {
|
|
40
31
|
padding: '0 0.51em',
|
|
41
32
|
backgroundColor: theme.background.paper,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { PartialElement } from '@furystack/shades'
|
|
2
2
|
import { Shade, createComponent, attachStyles } from '@furystack/shades'
|
|
3
3
|
import { ThemeProviderService } from '../..'
|
|
4
|
-
import type { Palette
|
|
4
|
+
import type { Palette } from '../../services'
|
|
5
5
|
|
|
6
6
|
export interface TextInputProps extends PartialElement<HTMLInputElement> {
|
|
7
7
|
/**
|
|
@@ -47,7 +47,6 @@ export interface TextInputProps extends PartialElement<HTMLInputElement> {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export type TextInputState = {
|
|
50
|
-
theme: Theme
|
|
51
50
|
value: string
|
|
52
51
|
focused: boolean
|
|
53
52
|
validity: ValidityState
|
|
@@ -69,12 +68,12 @@ const getLabelStyle = ({
|
|
|
69
68
|
justifyContent: 'space-between',
|
|
70
69
|
fontSize: '10px',
|
|
71
70
|
color: props.disabled
|
|
72
|
-
?
|
|
71
|
+
? themeProvider.theme.text.disabled
|
|
73
72
|
: state.validity?.valid === false
|
|
74
|
-
?
|
|
73
|
+
? themeProvider.theme.palette.error.main
|
|
75
74
|
: state.focused
|
|
76
|
-
?
|
|
77
|
-
:
|
|
75
|
+
? themeProvider.theme.text.primary
|
|
76
|
+
: themeProvider.theme.text.secondary,
|
|
78
77
|
marginBottom: '1em',
|
|
79
78
|
padding: '1em',
|
|
80
79
|
borderRadius: '5px',
|
|
@@ -83,8 +82,8 @@ const getLabelStyle = ({
|
|
|
83
82
|
? themeProvider
|
|
84
83
|
.getRgbFromColorString(
|
|
85
84
|
state.validity?.valid === false
|
|
86
|
-
?
|
|
87
|
-
:
|
|
85
|
+
? themeProvider.theme.palette.error.main
|
|
86
|
+
: themeProvider.theme.palette[props.defaultColor || 'primary'].main,
|
|
88
87
|
)
|
|
89
88
|
.update('a', state.focused ? 0.1 : 0.2)
|
|
90
89
|
.toString()
|
|
@@ -93,10 +92,10 @@ const getLabelStyle = ({
|
|
|
93
92
|
props.variant === 'outlined' || props.variant === 'contained'
|
|
94
93
|
? `0 0 0 1px ${
|
|
95
94
|
state.validity?.valid === false
|
|
96
|
-
?
|
|
95
|
+
? themeProvider.theme.palette.error.main
|
|
97
96
|
: state.focused
|
|
98
|
-
?
|
|
99
|
-
:
|
|
97
|
+
? themeProvider.theme.palette[props.defaultColor || 'primary'].main
|
|
98
|
+
: themeProvider.theme.text.primary
|
|
100
99
|
}`
|
|
101
100
|
: 'none',
|
|
102
101
|
filter: props.disabled ? 'grayscale(100%)' : 'none',
|
|
@@ -109,15 +108,11 @@ const getLabelStyle = ({
|
|
|
109
108
|
|
|
110
109
|
export const Input = Shade<TextInputProps, TextInputState>({
|
|
111
110
|
shadowDomName: 'shade-input',
|
|
112
|
-
getInitialState: ({
|
|
113
|
-
theme: injector.getInstance(ThemeProviderService).theme.getValue(),
|
|
111
|
+
getInitialState: ({ props }) => ({
|
|
114
112
|
value: props.value || '',
|
|
115
113
|
focused: props.autofocus || false,
|
|
116
114
|
validity: { valid: true } as ValidityState,
|
|
117
115
|
}),
|
|
118
|
-
resources: ({ injector, updateState }) => [
|
|
119
|
-
injector.getInstance(ThemeProviderService).theme.subscribe((theme) => updateState({ theme })),
|
|
120
|
-
],
|
|
121
116
|
compareState: ({ newState, element, props, injector }) => {
|
|
122
117
|
const themeProvider = injector.getInstance(ThemeProviderService)
|
|
123
118
|
const label = element.querySelector('label') as HTMLLabelElement
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { PartialElement } from '@furystack/shades'
|
|
2
2
|
import { createComponent, Shade } from '@furystack/shades'
|
|
3
|
-
import type { Theme } from '../../services'
|
|
4
3
|
import { ThemeProviderService } from '../../services'
|
|
5
4
|
import { promisifyAnimation } from '../../utils'
|
|
6
5
|
|
|
@@ -12,23 +11,18 @@ export interface TextAreaProps extends PartialElement<HTMLTextAreaElement> {
|
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
export type TextAreaInputState = {
|
|
15
|
-
theme: Theme
|
|
16
14
|
value?: string
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
export const TextArea = Shade<TextAreaProps, TextAreaInputState>({
|
|
20
18
|
shadowDomName: 'shade-text-area',
|
|
21
|
-
getInitialState: ({
|
|
22
|
-
theme: injector.getInstance(ThemeProviderService).theme.getValue(),
|
|
19
|
+
getInitialState: ({ props }) => ({
|
|
23
20
|
value: props.value,
|
|
24
21
|
}),
|
|
25
|
-
resources: ({ injector, updateState }) => {
|
|
26
|
-
const themeProvider = injector.getInstance(ThemeProviderService)
|
|
27
|
-
return [themeProvider.theme.subscribe((theme) => updateState({ theme }))]
|
|
28
|
-
},
|
|
29
22
|
render: ({ props, element, injector, getState }) => {
|
|
30
23
|
const themeProvider = injector.getInstance(ThemeProviderService)
|
|
31
|
-
const { theme
|
|
24
|
+
const { theme } = themeProvider
|
|
25
|
+
const { value } = getState()
|
|
32
26
|
const { palette } = theme
|
|
33
27
|
|
|
34
28
|
return (
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Shade, createComponent } from '@furystack/shades'
|
|
2
|
-
import { ThemeProviderService } from '../services'
|
|
3
2
|
import { promisifyAnimation } from '../utils'
|
|
4
3
|
|
|
5
4
|
interface LoaderProps {
|
|
@@ -15,14 +14,6 @@ interface LoaderProps {
|
|
|
15
14
|
|
|
16
15
|
export const Loader = Shade<LoaderProps>({
|
|
17
16
|
shadowDomName: 'shade-loader',
|
|
18
|
-
resources: ({ injector, element }) => [
|
|
19
|
-
injector.getInstance(ThemeProviderService).theme.subscribe((theme) => {
|
|
20
|
-
const el = element.firstElementChild
|
|
21
|
-
if (el) {
|
|
22
|
-
;(el as HTMLElement).style.borderBottom = `15px solid ${theme.palette.primary.main}`
|
|
23
|
-
}
|
|
24
|
-
}, true),
|
|
25
|
-
],
|
|
26
17
|
render: ({ element, props }) => {
|
|
27
18
|
element.style.display = 'inline-block'
|
|
28
19
|
element.style.transformOrigin = 'center'
|
|
@@ -42,7 +42,7 @@ export const NotyComponent = Shade<{ model: NotyModel; onDismiss: () => void }>(
|
|
|
42
42
|
},
|
|
43
43
|
render: ({ props, injector, element }) => {
|
|
44
44
|
const themeProvider = injector.getInstance(ThemeProviderService)
|
|
45
|
-
const colors = themeProvider.theme.
|
|
45
|
+
const colors = themeProvider.theme.palette[props.model.type]
|
|
46
46
|
const headerTextColor = themeProvider.getTextColor(colors.dark)
|
|
47
47
|
const textColor = themeProvider.getTextColor(colors.main)
|
|
48
48
|
|
|
@@ -51,8 +51,8 @@ export const NotyComponent = Shade<{ model: NotyModel; onDismiss: () => void }>(
|
|
|
51
51
|
await promisifyAnimation(
|
|
52
52
|
container,
|
|
53
53
|
[
|
|
54
|
-
{ opacity: '1', height: `${container?.scrollHeight || 0}px
|
|
55
|
-
{ opacity: '0', height: '0px' },
|
|
54
|
+
{ opacity: '1', height: `${container?.scrollHeight || 0}px`, margin: '8px 8px' },
|
|
55
|
+
{ opacity: '0', height: '0px', margin: '0px 8px' },
|
|
56
56
|
],
|
|
57
57
|
{
|
|
58
58
|
fill: 'forwards',
|
package/src/components/paper.tsx
CHANGED
|
@@ -4,15 +4,7 @@ import { ThemeProviderService } from '../services/theme-provider-service'
|
|
|
4
4
|
|
|
5
5
|
export const Paper = Shade<PartialElement<HTMLDivElement> & { elevation?: 1 | 2 | 3 }>({
|
|
6
6
|
shadowDomName: 'shade-paper',
|
|
7
|
-
|
|
8
|
-
const themeProvider = injector.getInstance(ThemeProviderService)
|
|
9
|
-
return [
|
|
10
|
-
themeProvider.theme.subscribe((newTheme) => {
|
|
11
|
-
;(element.firstChild as HTMLDivElement).style.background = newTheme.background.paper
|
|
12
|
-
;(element.firstChild as HTMLDivElement).style.color = themeProvider.theme.getValue().text.secondary
|
|
13
|
-
}),
|
|
14
|
-
]
|
|
15
|
-
},
|
|
7
|
+
|
|
16
8
|
render: ({ injector, props, children }) => {
|
|
17
9
|
const themeProvider = injector.getInstance(ThemeProviderService)
|
|
18
10
|
return (
|
|
@@ -21,8 +13,8 @@ export const Paper = Shade<PartialElement<HTMLDivElement> & { elevation?: 1 | 2
|
|
|
21
13
|
style={{
|
|
22
14
|
borderRadius: '3px',
|
|
23
15
|
boxShadow: props.elevation ? `1px ${props.elevation}px ${props.elevation}px rgba(0,0,0,0.3)` : '',
|
|
24
|
-
backgroundColor: themeProvider.theme.
|
|
25
|
-
color: themeProvider.theme.
|
|
16
|
+
backgroundColor: themeProvider.theme.background.paper,
|
|
17
|
+
color: themeProvider.theme.text.secondary,
|
|
26
18
|
margin: '8px',
|
|
27
19
|
padding: '6px 16px',
|
|
28
20
|
...props?.style,
|
package/src/components/tabs.tsx
CHANGED
|
@@ -19,7 +19,7 @@ export const Tabs = Shade<
|
|
|
19
19
|
>({
|
|
20
20
|
shadowDomName: 'shade-tabs',
|
|
21
21
|
getInitialState: ({ props }) => ({ activeIndex: props.activeTab || 0 }),
|
|
22
|
-
constructed: ({ injector, updateState
|
|
22
|
+
constructed: ({ injector, updateState }) => {
|
|
23
23
|
const subscriptions = [
|
|
24
24
|
injector.getInstance(LocationService).onLocationChanged.subscribe(() => {
|
|
25
25
|
const { hash } = location
|
|
@@ -28,20 +28,12 @@ export const Tabs = Shade<
|
|
|
28
28
|
page && updateState({ activeIndex: page })
|
|
29
29
|
}
|
|
30
30
|
}, true),
|
|
31
|
-
injector.getInstance(ThemeProviderService).theme.subscribe((t) => {
|
|
32
|
-
const headers = element.querySelectorAll('.shade-tabs-headers') as unknown as HTMLDivElement[]
|
|
33
|
-
headers.forEach((header) => {
|
|
34
|
-
const isActive = header.classList.contains('active')
|
|
35
|
-
header.style.backgroundColor = isActive ? t.background.paper : t.background.default
|
|
36
|
-
header.style.color = isActive ? t.text.primary : t.text.secondary
|
|
37
|
-
})
|
|
38
|
-
}),
|
|
39
31
|
]
|
|
40
32
|
return () => subscriptions.forEach((s) => s.dispose())
|
|
41
33
|
},
|
|
42
34
|
render: ({ props, getState, updateState, injector }) => {
|
|
43
35
|
const themeProvider = injector.getInstance(ThemeProviderService)
|
|
44
|
-
const theme = themeProvider
|
|
36
|
+
const { theme } = themeProvider
|
|
45
37
|
return (
|
|
46
38
|
<div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', ...props.containerStyle }}>
|
|
47
39
|
<div
|
|
@@ -12,6 +12,40 @@ export type EntryLoader<T> = <TFields extends Array<keyof T>>(
|
|
|
12
12
|
searchOptions: FindOptions<T, TFields>,
|
|
13
13
|
) => Promise<CollectionData<PartialResult<T, TFields>>>
|
|
14
14
|
|
|
15
|
+
export interface CollectionServiceOptions<T> {
|
|
16
|
+
/**
|
|
17
|
+
* A method used to retrieve the entries from the data source
|
|
18
|
+
*/
|
|
19
|
+
loader: EntryLoader<T>
|
|
20
|
+
/**
|
|
21
|
+
* The default filter / top / skip / etc... options
|
|
22
|
+
*/
|
|
23
|
+
defaultSettings: FindOptions<T, Array<keyof T>>
|
|
24
|
+
/**
|
|
25
|
+
* An optional field that can be used for quick search
|
|
26
|
+
*/
|
|
27
|
+
searchField?: keyof T
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param entry The clicked entry
|
|
31
|
+
* optional callback for row clicks
|
|
32
|
+
*/
|
|
33
|
+
onRowClick?: (entry: T) => void
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Optional callback for row double clicks
|
|
37
|
+
*
|
|
38
|
+
* @param entry The clicked entry
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
onRowDoubleClick?: (entry: T) => void
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* An optional debounce interval in milliseconds
|
|
45
|
+
*/
|
|
46
|
+
debounceMs?: number
|
|
47
|
+
}
|
|
48
|
+
|
|
15
49
|
export class CollectionService<T> implements Disposable {
|
|
16
50
|
public dispose() {
|
|
17
51
|
this.querySettings.dispose()
|
|
@@ -102,10 +136,12 @@ export class CollectionService<T> implements Disposable {
|
|
|
102
136
|
break
|
|
103
137
|
}
|
|
104
138
|
default:
|
|
105
|
-
if (this.searchField && ev.key.length === 1) {
|
|
139
|
+
if (this.options.searchField && ev.key.length === 1) {
|
|
106
140
|
const newSearchExpression = searchTerm + ev.key
|
|
107
141
|
const newFocusedEntry = entries.find(
|
|
108
|
-
(e) =>
|
|
142
|
+
(e) =>
|
|
143
|
+
this.options.searchField &&
|
|
144
|
+
(e[this.options.searchField] as any)?.toString().startsWith(newSearchExpression),
|
|
109
145
|
)
|
|
110
146
|
this.focusedEntry.setValue(newFocusedEntry)
|
|
111
147
|
this.searchTerm.setValue(newSearchExpression)
|
|
@@ -114,6 +150,7 @@ export class CollectionService<T> implements Disposable {
|
|
|
114
150
|
}
|
|
115
151
|
|
|
116
152
|
public handleRowClick(entry: T, ev: MouseEvent) {
|
|
153
|
+
this.options.onRowClick?.(entry)
|
|
117
154
|
const currentSelectionValue = this.selection.getValue()
|
|
118
155
|
const lastFocused = this.focusedEntry.getValue()
|
|
119
156
|
if (ev.ctrlKey) {
|
|
@@ -141,18 +178,19 @@ export class CollectionService<T> implements Disposable {
|
|
|
141
178
|
this.focusedEntry.setValue(entry)
|
|
142
179
|
}
|
|
143
180
|
|
|
144
|
-
constructor(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
181
|
+
constructor(private options: CollectionServiceOptions<T>) {
|
|
182
|
+
this.querySettings = new ObservableValue<FindOptions<T, Array<keyof T>>>(this.options.defaultSettings)
|
|
183
|
+
|
|
184
|
+
const loader = this.options.debounceMs
|
|
185
|
+
? debounce(this.options.loader, this.options.debounceMs)
|
|
186
|
+
: this.options.loader
|
|
187
|
+
|
|
188
|
+
this.getEntries = async (opt) => {
|
|
151
189
|
await this.loadLock.acquire()
|
|
152
190
|
try {
|
|
153
191
|
this.error.setValue(undefined)
|
|
154
192
|
this.isLoading.setValue(true)
|
|
155
|
-
const result = await
|
|
193
|
+
const result = await loader(opt)
|
|
156
194
|
this.data.setValue(result)
|
|
157
195
|
return result
|
|
158
196
|
} catch (error) {
|
|
@@ -162,7 +200,12 @@ export class CollectionService<T> implements Disposable {
|
|
|
162
200
|
this.loadLock.release()
|
|
163
201
|
this.isLoading.setValue(false)
|
|
164
202
|
}
|
|
165
|
-
}
|
|
203
|
+
}
|
|
204
|
+
|
|
166
205
|
this.querySettings.subscribe((val) => this.getEntries(val), true)
|
|
167
206
|
}
|
|
207
|
+
|
|
208
|
+
public async handleRowDoubleClick(entry: T) {
|
|
209
|
+
this.options.onRowDoubleClick?.(entry)
|
|
210
|
+
}
|
|
168
211
|
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { DeepPartial } from '@furystack/utils'
|
|
2
|
+
import type { Theme } from './theme-provider-service'
|
|
3
|
+
export const defaultVariableTheme: Theme = {
|
|
4
|
+
text: {
|
|
5
|
+
primary: 'var(--shades-theme-text-primary)',
|
|
6
|
+
secondary: 'var(--shades-theme-text-secondary)',
|
|
7
|
+
disabled: 'var(--shades-theme-text-disabled)',
|
|
8
|
+
},
|
|
9
|
+
button: {
|
|
10
|
+
active: 'var(--shades-theme-button-active)',
|
|
11
|
+
hover: 'var(--shades-theme-button-hover)',
|
|
12
|
+
selected: 'var(--shades-theme-button-selected)',
|
|
13
|
+
disabled: 'var(--shades-theme-button-disabled)',
|
|
14
|
+
disabledBackground: 'var(--shades-theme-button-disabled-background)',
|
|
15
|
+
},
|
|
16
|
+
background: {
|
|
17
|
+
default: 'var(--shades-theme-background-default)',
|
|
18
|
+
paper: 'var(--shades-theme-background-paper)',
|
|
19
|
+
},
|
|
20
|
+
palette: {
|
|
21
|
+
primary: {
|
|
22
|
+
light: 'var(--shades-theme-palette-primary-light)',
|
|
23
|
+
main: 'var(--shades-theme-palette-primary-main)',
|
|
24
|
+
dark: 'var(--shades-theme-palette-primary-dark)',
|
|
25
|
+
},
|
|
26
|
+
secondary: {
|
|
27
|
+
light: 'var(--shades-theme-palette-secondary-light)',
|
|
28
|
+
main: 'var(--shades-theme-palette-secondary-main)',
|
|
29
|
+
dark: 'var(--shades-theme-palette-secondary-dark)',
|
|
30
|
+
},
|
|
31
|
+
error: {
|
|
32
|
+
light: 'var(--shades-theme-palette-error-light)',
|
|
33
|
+
main: 'var(--shades-theme-palette-error-main)',
|
|
34
|
+
dark: 'var(--shades-theme-palette-error-dark)',
|
|
35
|
+
},
|
|
36
|
+
warning: {
|
|
37
|
+
light: 'var(--shades-theme-palette-warning-light)',
|
|
38
|
+
main: 'var(--shades-theme-palette-warning-main)',
|
|
39
|
+
dark: 'var(--shades-theme-palette-warning-dark)',
|
|
40
|
+
},
|
|
41
|
+
info: {
|
|
42
|
+
light: 'var(--shades-theme-palette-info-light)',
|
|
43
|
+
main: 'var(--shades-theme-palette-info-main)',
|
|
44
|
+
dark: 'var(--shades-theme-palette-info-dark)',
|
|
45
|
+
},
|
|
46
|
+
success: {
|
|
47
|
+
light: 'var(--shades-theme-palette-success-light)',
|
|
48
|
+
main: 'var(--shades-theme-palette-success-main)',
|
|
49
|
+
dark: 'var(--shades-theme-palette-success-dark)',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
divider: 'var(--shades-theme-divider)',
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const setCssVariable = (key: string, value: string, root: HTMLElement) => {
|
|
56
|
+
root.style.setProperty(key.replace('var(', '').replace(')', ''), value)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const getCssVariable = (key: string, root: HTMLElement = document.querySelector(':root') as HTMLElement) => {
|
|
60
|
+
return getComputedStyle(root).getPropertyValue(key.replace('var(', '').replace(')', ''))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const assignValue = <T extends object>(
|
|
64
|
+
target: T,
|
|
65
|
+
source: DeepPartial<T>,
|
|
66
|
+
root: HTMLElement,
|
|
67
|
+
assignFn = setCssVariable,
|
|
68
|
+
) => {
|
|
69
|
+
const keys = Object.keys(target) as Array<keyof T>
|
|
70
|
+
keys.forEach((key) => {
|
|
71
|
+
if (typeof source[key] === 'object' && typeof target[key] === 'object') {
|
|
72
|
+
assignValue(target[key] as object, source[key] as object, root)
|
|
73
|
+
return
|
|
74
|
+
} else {
|
|
75
|
+
assignFn(target[key] as string, source[key] as string, root)
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const useThemeCssVariables = (theme: DeepPartial<Theme>) => {
|
|
81
|
+
const root = document.querySelector(':root') as HTMLElement
|
|
82
|
+
assignValue(defaultVariableTheme, theme, root)
|
|
83
|
+
}
|
package/src/services/index.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Injectable } from '@furystack/inject'
|
|
2
2
|
import type { DeepPartial } from '@furystack/utils'
|
|
3
|
-
import {
|
|
4
|
-
import { defaultDarkTheme } from './default-dark-theme'
|
|
3
|
+
import { defaultVariableTheme, getCssVariable, useThemeCssVariables } from './default-variable-theme'
|
|
5
4
|
|
|
6
5
|
export type Color = string // `#${string}` | `rgba(${number},${number},${number},${number})` |
|
|
7
6
|
|
|
@@ -66,7 +65,7 @@ export class RgbColor {
|
|
|
66
65
|
@Injectable({ lifetime: 'singleton' })
|
|
67
66
|
export class ThemeProviderService {
|
|
68
67
|
/**
|
|
69
|
-
*
|
|
68
|
+
* @deprecated does not respect CSS vars
|
|
70
69
|
* @param color The background color
|
|
71
70
|
* @param bright The Bright color
|
|
72
71
|
* @param dark The Dark color
|
|
@@ -83,7 +82,11 @@ export class ThemeProviderService {
|
|
|
83
82
|
* @param color The color string
|
|
84
83
|
* @returns The parsed R,G,B, A values
|
|
85
84
|
*/
|
|
86
|
-
public getRgbFromColorString(color: string) {
|
|
85
|
+
public getRgbFromColorString(color: string): RgbColor {
|
|
86
|
+
if (color.startsWith('var(--')) {
|
|
87
|
+
return this.getRgbFromColorString(getCssVariable(color))
|
|
88
|
+
}
|
|
89
|
+
|
|
87
90
|
if (color.startsWith('#')) {
|
|
88
91
|
if (color.length === 7) {
|
|
89
92
|
const r = parseInt(color.substr(1, 2), 16)
|
|
@@ -111,16 +114,16 @@ export class ThemeProviderService {
|
|
|
111
114
|
)
|
|
112
115
|
}
|
|
113
116
|
}
|
|
114
|
-
throw Error(`Color format '${color} is not supported.'`)
|
|
117
|
+
throw Error(`Color format '${color}' is not supported.'`)
|
|
115
118
|
}
|
|
116
119
|
|
|
117
|
-
public readonly theme =
|
|
120
|
+
public readonly theme = defaultVariableTheme
|
|
118
121
|
|
|
119
122
|
public set(change: DeepPartial<Theme>) {
|
|
120
|
-
|
|
123
|
+
useThemeCssVariables(change)
|
|
121
124
|
}
|
|
122
125
|
|
|
123
126
|
public dispose() {
|
|
124
|
-
|
|
127
|
+
/** */
|
|
125
128
|
}
|
|
126
129
|
}
|