@furystack/shades-common-components 3.0.2 → 3.1.0

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.
Files changed (90) hide show
  1. package/dist/components/app-bar-link.js +39 -0
  2. package/dist/components/app-bar-link.js.map +1 -0
  3. package/dist/components/app-bar.js +1 -2
  4. package/dist/components/app-bar.js.map +1 -1
  5. package/dist/components/button.js +17 -8
  6. package/dist/components/button.js.map +1 -1
  7. package/dist/components/command-palette/command-palette-manager.js +0 -2
  8. package/dist/components/command-palette/command-palette-manager.js.map +1 -1
  9. package/dist/components/command-palette/index.js +57 -55
  10. package/dist/components/command-palette/index.js.map +1 -1
  11. package/dist/components/data-grid/body.js +6 -26
  12. package/dist/components/data-grid/body.js.map +1 -1
  13. package/dist/components/data-grid/data-grid-row.js +62 -0
  14. package/dist/components/data-grid/data-grid-row.js.map +1 -0
  15. package/dist/components/data-grid/data-grid.js +16 -17
  16. package/dist/components/data-grid/data-grid.js.map +1 -1
  17. package/dist/components/data-grid/footer.js +8 -6
  18. package/dist/components/data-grid/footer.js.map +1 -1
  19. package/dist/components/data-grid/header.js +1 -1
  20. package/dist/components/data-grid/header.js.map +1 -1
  21. package/dist/components/data-grid/index.js +1 -0
  22. package/dist/components/data-grid/index.js.map +1 -1
  23. package/dist/components/data-grid/selection-cell.js +32 -0
  24. package/dist/components/data-grid/selection-cell.js.map +1 -0
  25. package/dist/components/grid.js +1 -3
  26. package/dist/components/grid.js.map +1 -1
  27. package/dist/components/index.js +1 -0
  28. package/dist/components/index.js.map +1 -1
  29. package/dist/components/input.js +14 -5
  30. package/dist/components/input.js.map +1 -1
  31. package/dist/components/paper.js +1 -1
  32. package/dist/components/paper.js.map +1 -1
  33. package/dist/services/click-away-service.js +1 -1
  34. package/dist/services/click-away-service.js.map +1 -1
  35. package/dist/services/collection-service.js +80 -2
  36. package/dist/services/collection-service.js.map +1 -1
  37. package/dist/services/default-palette.js +1 -1
  38. package/dist/services/theme-provider-service.js +18 -2
  39. package/dist/services/theme-provider-service.js.map +1 -1
  40. package/package.json +7 -6
  41. package/src/components/app-bar-link.tsx +44 -0
  42. package/src/components/app-bar.tsx +1 -2
  43. package/src/components/button.tsx +21 -13
  44. package/src/components/command-palette/command-palette-manager.ts +0 -6
  45. package/src/components/command-palette/index.tsx +55 -53
  46. package/src/components/data-grid/body.tsx +15 -40
  47. package/src/components/data-grid/data-grid-row.tsx +88 -0
  48. package/src/components/data-grid/data-grid.tsx +25 -32
  49. package/src/components/data-grid/footer.tsx +22 -20
  50. package/src/components/data-grid/header.tsx +1 -1
  51. package/src/components/data-grid/index.tsx +1 -0
  52. package/src/components/data-grid/selection-cell.tsx +32 -0
  53. package/src/components/grid.tsx +1 -1
  54. package/src/components/index.ts +1 -0
  55. package/src/components/input.tsx +22 -5
  56. package/src/components/paper.tsx +1 -1
  57. package/src/services/click-away-service.ts +1 -1
  58. package/src/services/collection-service.ts +88 -2
  59. package/src/services/default-palette.ts +1 -1
  60. package/src/services/theme-provider-service.ts +18 -4
  61. package/tsconfig.json +2 -1
  62. package/tsconfig.tsbuildinfo +1 -1
  63. package/types/components/animations.d.ts +2 -2
  64. package/types/components/animations.d.ts.map +1 -1
  65. package/types/components/app-bar-link.d.ts +3 -0
  66. package/types/components/app-bar-link.d.ts.map +1 -0
  67. package/types/components/app-bar.d.ts.map +1 -1
  68. package/types/components/button.d.ts.map +1 -1
  69. package/types/components/command-palette/command-palette-manager.d.ts +0 -3
  70. package/types/components/command-palette/command-palette-manager.d.ts.map +1 -1
  71. package/types/components/command-palette/index.d.ts.map +1 -1
  72. package/types/components/data-grid/body.d.ts +0 -2
  73. package/types/components/data-grid/body.d.ts.map +1 -1
  74. package/types/components/data-grid/data-grid-row.d.ts +15 -0
  75. package/types/components/data-grid/data-grid-row.d.ts.map +1 -0
  76. package/types/components/data-grid/data-grid.d.ts +4 -9
  77. package/types/components/data-grid/data-grid.d.ts.map +1 -1
  78. package/types/components/data-grid/footer.d.ts.map +1 -1
  79. package/types/components/data-grid/index.d.ts +1 -0
  80. package/types/components/data-grid/index.d.ts.map +1 -1
  81. package/types/components/data-grid/selection-cell.d.ts +6 -0
  82. package/types/components/data-grid/selection-cell.d.ts.map +1 -0
  83. package/types/components/index.d.ts +1 -0
  84. package/types/components/index.d.ts.map +1 -1
  85. package/types/components/input.d.ts +5 -0
  86. package/types/components/input.d.ts.map +1 -1
  87. package/types/services/collection-service.d.ts +4 -1
  88. package/types/services/collection-service.d.ts.map +1 -1
  89. package/types/services/theme-provider-service.d.ts +22 -2
  90. package/types/services/theme-provider-service.d.ts.map +1 -1
@@ -1,6 +1,5 @@
1
1
  import { Injector, Injectable } from '@furystack/inject'
2
2
  import { debounce, ObservableValue } from '@furystack/utils'
3
- import { ClickAwayService } from '../../services/click-away-service'
4
3
  import { CommandProvider, CommandPaletteSuggestionResult } from './command-provider'
5
4
 
6
5
  @Injectable({ lifetime: 'singleton' })
@@ -21,8 +20,6 @@ export class CommandPaletteManager {
21
20
  }
22
21
  }).bind(this)
23
22
 
24
- public element?: HTMLElement
25
-
26
23
  public dispose() {
27
24
  window.removeEventListener('keyup', this.keyPressListener)
28
25
  this.isOpened.dispose()
@@ -30,7 +27,6 @@ export class CommandPaletteManager {
30
27
  this.term.dispose()
31
28
  this.selectedIndex.dispose()
32
29
  this.currentSuggestions.dispose()
33
- this.clickAwayListener?.dispose()
34
30
  }
35
31
 
36
32
  public selectSuggestion(injector: Injector, index: number = this.selectedIndex.getValue()) {
@@ -62,8 +58,6 @@ export class CommandPaletteManager {
62
58
  }
63
59
  }, 250)
64
60
 
65
- public clickAwayListener?: ClickAwayService<any>
66
-
67
61
  constructor(private readonly commandProviders: CommandProvider[]) {
68
62
  window.addEventListener('keyup', this.keyPressListener, true)
69
63
  }
@@ -28,64 +28,66 @@ export const CommandPalette = Shade<CommandPaletteProps, CommandPaletteState>({
28
28
  getInitialState: ({ props }) => ({
29
29
  manager: new CommandPaletteManager(props.commandProviders),
30
30
  }),
31
- constructed: ({ element: el, getState }) => {
31
+ resources: ({ getState, element: rootElement }) => {
32
32
  const { manager } = getState()
33
- const element = el.querySelector('.input-container') as HTMLDivElement
34
- manager.element = el
35
- manager.clickAwayListener = new ClickAwayService(el, () => manager.isOpened.setValue(false))
36
- manager.isOpened.subscribe((isOpened) => {
37
- const suggestions = el.querySelector('.close-suggestions')
38
- const postControls = el.querySelector('.post-controls')
39
- if (isOpened) {
40
- promisifyAnimation(suggestions, [{ opacity: 0 }, { opacity: 1 }], {
41
- duration: 500,
42
- fill: 'forwards',
43
- })
33
+ const element = rootElement.querySelector('.input-container') as HTMLDivElement
34
+ const clickAwayListener = new ClickAwayService(rootElement, () => manager.isOpened.setValue(false))
35
+ return [
36
+ manager.isOpened.subscribe((isOpened) => {
37
+ const suggestions = rootElement.querySelector('.close-suggestions')
38
+ const postControls = rootElement.querySelector('.post-controls')
39
+ if (isOpened) {
40
+ promisifyAnimation(suggestions, [{ opacity: 0 }, { opacity: 1 }], {
41
+ duration: 500,
42
+ fill: 'forwards',
43
+ })
44
44
 
45
- promisifyAnimation(postControls, [{ width: '0px' }, { width: '50px' }], {
46
- duration: 100,
47
- fill: 'forwards',
48
- })
45
+ promisifyAnimation(postControls, [{ width: '0px' }, { width: '50px' }], {
46
+ duration: 100,
47
+ fill: 'forwards',
48
+ })
49
49
 
50
- promisifyAnimation(element, [{ background: 'transparent' }, { background: 'rgba(128,128,128,0.1)' }], {
51
- duration: 500,
52
- fill: 'forwards',
53
- easing: 'cubic-bezier(0.050, 0.570, 0.840, 1.005)',
54
- })
55
- } else {
56
- promisifyAnimation(suggestions, [{ opacity: 1 }, { opacity: 0 }], {
57
- duration: 500,
58
- fill: 'forwards',
59
- })
50
+ promisifyAnimation(element, [{ background: 'transparent' }, { background: 'rgba(128,128,128,0.1)' }], {
51
+ duration: 500,
52
+ fill: 'forwards',
53
+ easing: 'cubic-bezier(0.050, 0.570, 0.840, 1.005)',
54
+ })
55
+ } else {
56
+ promisifyAnimation(suggestions, [{ opacity: 1 }, { opacity: 0 }], {
57
+ duration: 500,
58
+ fill: 'forwards',
59
+ })
60
60
 
61
- promisifyAnimation(postControls, [{ width: '50px' }, { width: '0px' }], {
62
- duration: 500,
63
- fill: 'forwards',
64
- delay: 300,
65
- })
61
+ promisifyAnimation(postControls, [{ width: '50px' }, { width: '0px' }], {
62
+ duration: 500,
63
+ fill: 'forwards',
64
+ delay: 300,
65
+ })
66
66
 
67
- promisifyAnimation(element, [{ background: 'rgba(128,128,128,0.1)' }, { background: 'transparent' }], {
68
- duration: 300,
69
- fill: 'forwards',
70
- easing: 'cubic-bezier(0.000, 0.245, 0.190, 0.790)',
71
- })
72
- }
73
- })
74
- manager.isLoading.subscribe(async (isLoading) => {
75
- const loader = el.querySelector('.loader-container')
76
- if (isLoading) {
77
- promisifyAnimation(loader, [{ opacity: 0 }, { opacity: 1 }], {
78
- duration: 100,
79
- fill: 'forwards',
80
- })
81
- } else {
82
- promisifyAnimation(loader, [{ opacity: 1 }, { opacity: 0 }], {
83
- duration: 100,
84
- fill: 'forwards',
85
- })
86
- }
87
- })
88
- return () => manager.dispose()
67
+ promisifyAnimation(element, [{ background: 'rgba(128,128,128,0.1)' }, { background: 'transparent' }], {
68
+ duration: 300,
69
+ fill: 'forwards',
70
+ easing: 'cubic-bezier(0.000, 0.245, 0.190, 0.790)',
71
+ })
72
+ }
73
+ }),
74
+ manager.isLoading.subscribe(async (isLoading) => {
75
+ const loader = rootElement.querySelector('.loader-container')
76
+ if (isLoading) {
77
+ promisifyAnimation(loader, [{ opacity: 0 }, { opacity: 1 }], {
78
+ duration: 100,
79
+ fill: 'forwards',
80
+ })
81
+ } else {
82
+ promisifyAnimation(loader, [{ opacity: 1 }, { opacity: 0 }], {
83
+ duration: 100,
84
+ fill: 'forwards',
85
+ })
86
+ }
87
+ }),
88
+ clickAwayListener,
89
+ manager,
90
+ ]
89
91
  },
90
92
  render: ({ props, injector, element, getState }) => {
91
93
  element.style.flexGrow = '1'
@@ -1,7 +1,8 @@
1
1
  import { CollectionService } from '../../services/collection-service'
2
- import { ChildrenList, Shade, createComponent } from '@furystack/shades'
2
+ import { ChildrenList, Shade, createComponent, createFragment } from '@furystack/shades'
3
3
  import { DataRowCells } from './data-grid'
4
4
  import { Loader } from '../loader'
5
+ import { DataGridRow } from './data-grid-row'
5
6
 
6
7
  export interface DataGridBodyProps<T> {
7
8
  service: CollectionService<T>
@@ -13,8 +14,6 @@ export interface DataGridBodyProps<T> {
13
14
 
14
15
  export interface DataGridBodyState<T> {
15
16
  data: T[]
16
- selection: T[]
17
- focus: T | undefined
18
17
  isLoading: boolean
19
18
  }
20
19
 
@@ -24,19 +23,12 @@ export const DataGridBody: <T>(props: DataGridBodyProps<T>, children: ChildrenLi
24
23
  >({
25
24
  getInitialState: ({ props }) => ({
26
25
  data: props.service.data.getValue().entries,
27
- selection: props.service.selection.getValue(),
28
- focus: props.service.focus.getValue(),
29
26
  isLoading: props.service.isLoading.getValue(),
30
27
  }),
31
- constructed: ({ props, updateState }) => {
32
- const disposables = [
33
- props.service.data.subscribe((data) => updateState({ data: data.entries })),
34
- props.service.focus.subscribe((focus) => updateState({ focus })),
35
- props.service.selection.subscribe((selection) => updateState({ selection })),
36
- props.service.isLoading.subscribe((isLoading) => updateState({ isLoading })),
37
- ]
38
- return () => disposables.map((d) => d.dispose())
39
- },
28
+ resources: ({ props, updateState }) => [
29
+ props.service.data.subscribe((data) => updateState({ data: data.entries })),
30
+ props.service.isLoading.subscribe((isLoading) => updateState({ isLoading })),
31
+ ],
40
32
  shadowDomName: 'shade-data-grid-body',
41
33
  render: ({ getState, props, element }) => {
42
34
  element.style.display = 'table-row-group'
@@ -45,6 +37,7 @@ export const DataGridBody: <T>(props: DataGridBodyProps<T>, children: ChildrenLi
45
37
  if (state.isLoading) {
46
38
  return (
47
39
  <div style={{ display: 'flex', height: '100%', justifyContent: 'center', alignItems: 'center', width: '100%' }}>
40
+ {/* TODO: Skeleton */}
48
41
  <Loader style={{ height: '128px', width: '128px' }} />
49
42
  </div>
50
43
  )
@@ -55,34 +48,16 @@ export const DataGridBody: <T>(props: DataGridBodyProps<T>, children: ChildrenLi
55
48
  }
56
49
 
57
50
  return (
58
- <div style={{ display: 'contents' }}>
51
+ <>
59
52
  {state.data.map((entry) => (
60
- <tr
61
- style={{
62
- background: state.selection.includes(entry) ? 'rgba(128,128,128,0.3)' : 'transparent',
63
- filter: state.focus === entry ? 'brightness(1.5)' : 'brightness(1)',
64
- cursor: 'default',
65
- boxShadow: '2px 1px 0px rgba(255,255,255,0.07)',
66
- fontSize: '0.8em',
67
- }}
68
- onclick={() => {
69
- if (getState().focus !== entry) {
70
- props.service.focus.setValue(entry)
71
- props.service.selection.setValue([entry])
72
- }
73
- }}
74
- ondblclick={() => props.onDoubleClick?.(entry)}
75
- >
76
- {props.columns.map((column: any) => (
77
- <td style={{ padding: '0.5em', ...props.style }}>
78
- {props.rowComponents?.[column]?.(entry, state) || props.rowComponents?.default?.(entry, state) || (
79
- <span>{entry[column]}</span>
80
- )}
81
- </td>
82
- ))}
83
- </tr>
53
+ <DataGridRow<any>
54
+ columns={props.columns}
55
+ entry={entry}
56
+ service={props.service}
57
+ rowComponents={props.rowComponents}
58
+ ></DataGridRow>
84
59
  ))}
85
- </div>
60
+ </>
86
61
  )
87
62
  },
88
63
  })
@@ -0,0 +1,88 @@
1
+ import { ChildrenList, createComponent, createFragment, Shade } from '@furystack/shades'
2
+ import { CollectionService } from '../../services/collection-service'
3
+ import { DataRowCells } from './data-grid'
4
+
5
+ export interface DataGridRowProps<T> {
6
+ entry: T
7
+ columns: Array<keyof T>
8
+ service: CollectionService<T>
9
+ rowComponents?: DataRowCells<T>
10
+ }
11
+
12
+ export interface DataGridRowState<T> {
13
+ selection?: T[]
14
+ focus?: T
15
+ }
16
+
17
+ export const DataGridRow: <T>(props: DataGridRowProps<T>, children: ChildrenList) => JSX.Element<any, any> = Shade<
18
+ DataGridRowProps<any>,
19
+ DataGridRowState<any>
20
+ >({
21
+ getInitialState: ({ props }) => ({
22
+ focus: props.service.focusedEntry.getValue(),
23
+ selection: props.service.selection.getValue(),
24
+ }),
25
+ shadowDomName: 'shades-data-grid-row',
26
+ resources: ({ props, element }) => [
27
+ props.service.focusedEntry.subscribe((newEntry) => {
28
+ if (newEntry === props.entry) {
29
+ element.style.filter = 'brightness(1.5)'
30
+ element.style.fontWeight = 'bolder'
31
+
32
+ const headerHeight = element.closest('table')?.querySelector('th')?.getBoundingClientRect().height || 42
33
+
34
+ const parent = element.closest('.shade-grid-wrapper') as HTMLElement
35
+ const maxTop = element.offsetTop - headerHeight
36
+ const currentTop = parent.scrollTop
37
+ if (maxTop < currentTop) {
38
+ parent.scrollTo({ top: maxTop, behavior: 'smooth' })
39
+ }
40
+
41
+ const footerHeight =
42
+ element.closest('shade-data-grid')?.querySelector('shade-data-grid-footer')?.getBoundingClientRect().height ||
43
+ 42
44
+ const visibleMaxTop = parent.clientHeight - footerHeight // parent.getBoundingClientRect().height - footerHeight - headerHeight
45
+ const desiredMaxTop = element.offsetTop + element.clientHeight
46
+ if (desiredMaxTop > visibleMaxTop) {
47
+ parent.scrollTo({ top: desiredMaxTop - visibleMaxTop, behavior: 'smooth' })
48
+ }
49
+
50
+ // ;(element as any).scrollIntoView({ inline: 'nearest', block: 'nearest', behavior: 'smooth' })
51
+ } else {
52
+ element.style.filter = 'brightness(1)'
53
+ element.style.fontWeight = 'inherit'
54
+ }
55
+ }),
56
+ props.service.selection.subscribe((selection) => {
57
+ if (selection.includes(props.entry)) {
58
+ element.style.background = 'rgba(128,128,128,0.1)'
59
+ } else {
60
+ element.style.background = 'none'
61
+ }
62
+ }),
63
+ ],
64
+
65
+ render: ({ getState, props, element }) => {
66
+ const state = getState()
67
+ const { entry, rowComponents, columns } = props
68
+
69
+ element.style.display = 'table-row'
70
+ element.style.cursor = 'default'
71
+ element.style.userSelect = 'none'
72
+
73
+ element.onclick = () => {
74
+ props.service.focusedEntry.setValue(props.entry)
75
+ }
76
+ return (
77
+ <>
78
+ {columns.map((column) => (
79
+ <td style={{ padding: '0.5em' }}>
80
+ {rowComponents?.[column]?.(entry, state) || rowComponents?.default?.(entry, state) || (
81
+ <span>{entry[column]}</span>
82
+ )}
83
+ </td>
84
+ ))}
85
+ </>
86
+ )
87
+ },
88
+ })
@@ -1,17 +1,17 @@
1
1
  import { ChildrenList, createComponent, Shade } from '@furystack/shades'
2
2
  import { CollectionService } from '../../services/collection-service'
3
3
  import { GridProps } from '../grid'
4
- import { colors } from '../styles'
5
4
  import { DataGridHeader } from './header'
6
- import { DataGridBody, DataGridBodyState } from './body'
5
+ import { DataGridBody } from './body'
7
6
  import { DataGridFooter } from './footer'
8
- import { ThemeProviderService } from '../../services'
7
+ import { ClickAwayService, ThemeProviderService } from '../../services'
8
+ import { DataGridRowState } from './data-grid-row'
9
9
 
10
10
  export type DataHeaderCells<T> = {
11
- [TKey in keyof T | 'default']?: (name: keyof T, state: DataGridState) => JSX.Element
11
+ [TKey in keyof T | 'default']?: (name: keyof T) => JSX.Element
12
12
  }
13
13
  export type DataRowCells<T> = {
14
- [TKey in keyof T | 'default']?: (element: T, state: DataGridBodyState<T>) => JSX.Element
14
+ [TKey in keyof T | 'default']?: (element: T, state: DataGridRowState<T>) => JSX.Element
15
15
  }
16
16
 
17
17
  export interface DataGridProps<T> {
@@ -20,25 +20,16 @@ export interface DataGridProps<T> {
20
20
  service: CollectionService<T>
21
21
  headerComponents: DataHeaderCells<T>
22
22
  rowComponents: DataRowCells<T>
23
- onFocusChange?: (entry?: T) => void
24
- onSelectionChange?: (selection: T[]) => void
25
- onDoubleClick?: (entry: T) => void
26
- }
27
-
28
- export interface DataGridState {
29
- error?: unknown
23
+ autofocus?: boolean
30
24
  }
31
25
 
32
26
  export const DataGrid: <T>(props: DataGridProps<T>, children: ChildrenList) => JSX.Element<any, any> = Shade<
33
- DataGridProps<any>,
34
- DataGridState
27
+ DataGridProps<any>
35
28
  >({
36
29
  shadowDomName: 'shade-data-grid',
37
- getInitialState: () => ({}),
38
- constructed: ({ props, updateState, injector, element }) => {
30
+ resources: ({ injector, element, props }) => {
39
31
  const tp = injector.getInstance(ThemeProviderService)
40
- const subscriptions = [
41
- props.service.error.subscribe((error) => updateState({ error })),
32
+ return [
42
33
  tp.theme.subscribe((t) => {
43
34
  const headers = element.querySelectorAll('th')
44
35
  const { r, g, b } = tp.getRgbFromColorString(t.background.paper)
@@ -47,18 +38,19 @@ export const DataGrid: <T>(props: DataGridProps<T>, children: ChildrenList) => J
47
38
  header.style.backgroundColor = `rgba(${r}, ${g}, ${b}, 0.3)`
48
39
  })
49
40
  }),
50
- props.service.focus.subscribe((f) => props.onFocusChange?.(f)),
51
- props.service.selection.subscribe((f) => props.onSelectionChange?.(f)),
41
+ new ClickAwayService(element, () => {
42
+ props.service.hasFocus.setValue(false)
43
+ }),
52
44
  ]
53
- return () => Promise.all(subscriptions.map((s) => s.dispose()))
54
45
  },
55
- render: ({ props, getState, injector }) => {
46
+ constructed: ({ props }) => {
47
+ window.addEventListener('keydown', (ev) => {
48
+ props.service.handleKeyDown(ev)
49
+ })
50
+ },
51
+ render: ({ props, injector }) => {
56
52
  const tp = injector.getInstance(ThemeProviderService)
57
53
  const theme = tp.theme.getValue()
58
- const state = getState()
59
- if (state.error) {
60
- return <div style={{ color: colors.error.main }}>{JSON.stringify(state.error)}</div>
61
- }
62
54
 
63
55
  const { r, g, b } = tp.getRgbFromColorString(theme.background.paper)
64
56
  const headerStyle: Partial<CSSStyleDeclaration> = {
@@ -86,17 +78,19 @@ export const DataGrid: <T>(props: DataGridProps<T>, children: ChildrenList) => J
86
78
  overflow: 'auto',
87
79
  zIndex: '1',
88
80
  }}
81
+ onclick={() => {
82
+ props.service.hasFocus.setValue(true)
83
+ }}
89
84
  >
90
- <table style={{ width: '100%', height: 'calc(100% - 4em)', position: 'relative' }}>
85
+ <table style={{ width: '100%', maxHeight: 'calc(100% - 4em)', position: 'relative' }}>
91
86
  <thead>
92
87
  <tr>
93
88
  {props.columns.map((column: any) => {
94
89
  return (
95
90
  <th style={headerStyle}>
96
- {props.headerComponents?.[column]?.(column, state) ||
97
- props.headerComponents?.default?.(column, state) || (
98
- <DataGridHeader<any, typeof column> field={column} collectionService={props.service} />
99
- )}
91
+ {props.headerComponents?.[column]?.(column) || props.headerComponents?.default?.(column) || (
92
+ <DataGridHeader<any, typeof column> field={column} collectionService={props.service} />
93
+ )}
100
94
  </th>
101
95
  )
102
96
  })}
@@ -107,7 +101,6 @@ export const DataGrid: <T>(props: DataGridProps<T>, children: ChildrenList) => J
107
101
  service={props.service}
108
102
  rowComponents={props.rowComponents}
109
103
  style={props.styles?.cell}
110
- onDoubleClick={props.onDoubleClick}
111
104
  />
112
105
  </table>
113
106
  <DataGridFooter service={props.service} />
@@ -2,7 +2,7 @@ import { Shade, createComponent } from '@furystack/shades'
2
2
  import { ThemeProviderService } from '../../services'
3
3
  import { CollectionService, CollectionData } from '../../services/collection-service'
4
4
 
5
- export const dataGridItemsPerPage = [10, 20, 25, 50, 100]
5
+ export const dataGridItemsPerPage = [10, 20, 25, 50, 100, Infinity]
6
6
 
7
7
  export const DataGridFooter = Shade<{ service: CollectionService<any> }, { data: CollectionData<any> }>({
8
8
  shadowDomName: 'shade-data-grid-footer',
@@ -26,13 +26,14 @@ export const DataGridFooter = Shade<{ service: CollectionService<any> }, { data:
26
26
  const state = getState()
27
27
  const currentQuerySettings = props.service.querySettings.getValue()
28
28
  const currentPage = Math.ceil(currentQuerySettings.skip || 0) / (currentQuerySettings.top || 1)
29
+ const currentEntriesPerPage = currentQuerySettings.top || Infinity
29
30
  const theme = injector.getInstance(ThemeProviderService).theme.getValue()
30
31
 
31
32
  return (
32
33
  <div
33
34
  className="pager"
34
35
  style={{
35
- background: theme.background.paper,
36
+ backdropFilter: 'blur(10px)',
36
37
  color: theme.text.secondary,
37
38
  position: 'sticky',
38
39
  bottom: '0',
@@ -42,27 +43,28 @@ export const DataGridFooter = Shade<{ service: CollectionService<any> }, { data:
42
43
  alignItems: 'center',
43
44
  }}
44
45
  >
45
- <div>
46
- Goto page
47
- <select
48
- style={{ margin: '0 1em' }}
49
- onchange={(ev) => {
50
- const value = parseInt((ev.target as any).value, 10)
51
- const currentQuery = props.service.querySettings.getValue()
52
- props.service.querySettings.setValue({ ...currentQuery, skip: (currentQuery.top || 0) * value })
53
- }}
54
- >
55
- {[...new Array(Math.ceil(state.data.count / (props.service.querySettings.getValue().top || Infinity)))].map(
56
- (_val, index) => (
46
+ {currentEntriesPerPage !== Infinity && (
47
+ <div>
48
+ Goto page
49
+ <select
50
+ style={{ margin: '0 1em' }}
51
+ onchange={(ev) => {
52
+ const value = parseInt((ev.target as any).value, 10)
53
+ const currentQuery = props.service.querySettings.getValue()
54
+ props.service.querySettings.setValue({ ...currentQuery, skip: (currentQuery.top || 0) * value })
55
+ }}
56
+ >
57
+ {[
58
+ ...new Array(Math.ceil(state.data.count / (props.service.querySettings.getValue().top || Infinity))),
59
+ ].map((_val, index) => (
57
60
  <option value={index.toString()} selected={currentPage === index}>
58
61
  {(index + 1).toString()}
59
62
  </option>
60
- ),
61
- )}
62
- </select>
63
- </div>
63
+ ))}
64
+ </select>
65
+ </div>
66
+ )}
64
67
  <div>
65
- {' '}
66
68
  Show
67
69
  <select
68
70
  style={{ margin: '0 1em' }}
@@ -76,7 +78,7 @@ export const DataGridFooter = Shade<{ service: CollectionService<any> }, { data:
76
78
  }}
77
79
  >
78
80
  {dataGridItemsPerPage.map((no) => (
79
- <option value={no.toString()} selected={no === currentQuerySettings.top}>
81
+ <option value={no.toString()} selected={no === currentEntriesPerPage}>
80
82
  {no.toString()}
81
83
  </option>
82
84
  ))}
@@ -29,7 +29,7 @@ export const DataGridHeader: <T, K extends keyof T>(
29
29
  ...currentSettings,
30
30
  filter: {
31
31
  ...currentSettings.filter,
32
- [props.field]: value ? { $regex: value } : undefined,
32
+ [props.field]: { $regex: value },
33
33
  },
34
34
  }
35
35
  props.collectionService.querySettings.setValue(newSettings)
@@ -1 +1,2 @@
1
1
  export * from './data-grid'
2
+ export * from './selection-cell'
@@ -0,0 +1,32 @@
1
+ import { createComponent, Shade } from '@furystack/shades'
2
+ import { CollectionService } from '../../services'
3
+
4
+ export const SelectionCell = Shade<{ entry: any; service: CollectionService<any> }>({
5
+ shadowDomName: 'shades-data-grid-selection-cell',
6
+ resources: ({ props, element }) => [
7
+ props.service.selection.subscribe((selection) => {
8
+ if (selection.includes(props.entry)) {
9
+ ;(element.firstChild as HTMLInputElement).checked = true
10
+ } else {
11
+ ;(element.firstChild as HTMLInputElement).checked = false
12
+ }
13
+ }),
14
+ ],
15
+ render: ({ props }) => {
16
+ return (
17
+ <input
18
+ onchange={(ev) => {
19
+ if ((ev.target as HTMLInputElement).checked) {
20
+ props.service.selection.setValue([...props.service.selection.getValue(), props.entry])
21
+ } else {
22
+ props.service.selection.setValue([
23
+ ...props.service.selection.getValue().filter((entry) => entry !== props.entry),
24
+ ])
25
+ }
26
+ }}
27
+ type="checkbox"
28
+ checked={props.service.selection.getValue().includes(props.entry) ? true : false}
29
+ />
30
+ )
31
+ },
32
+ })
@@ -64,7 +64,7 @@ export const Grid: <T>(props: GridProps<T>, children: ChildrenList) => JSX.Eleme
64
64
  <th style={headerStyle}>
65
65
  {props.headerComponents?.[column]?.(column) || props.headerComponents?.default?.(column) || (
66
66
  <span>{column}</span>
67
- )}{' '}
67
+ )}
68
68
  </th>
69
69
  )
70
70
  })}
@@ -1,5 +1,6 @@
1
1
  export * from './animations'
2
2
  export * from './app-bar'
3
+ export * from './app-bar-link'
3
4
  export * from './autocomplete'
4
5
  export * from './avatar'
5
6
  export * from './button'
@@ -1,5 +1,6 @@
1
1
  import { Shade, PartialElement, createComponent } from '@furystack/shades'
2
2
  import { ThemeProviderService } from '..'
3
+ import { Theme } from '../services'
3
4
  import { promisifyAnimation } from '../utils/promisify-animation'
4
5
 
5
6
  export interface InputProps extends PartialElement<HTMLInputElement> {
@@ -18,11 +19,24 @@ export interface TextAreaProps extends PartialElement<HTMLTextAreaElement> {
18
19
  }
19
20
 
20
21
  export type TextInputProps = InputProps | TextAreaProps
21
- export const Input = Shade<TextInputProps>({
22
+
23
+ export type TextInputState = {
24
+ theme: Theme
25
+ value?: string
26
+ }
27
+
28
+ export const Input = Shade<TextInputProps, TextInputState>({
22
29
  shadowDomName: 'shade-input',
23
- render: ({ props, element, injector }) => {
30
+ getInitialState: ({ injector, props }) => ({
31
+ theme: injector.getInstance(ThemeProviderService).theme.getValue(),
32
+ value: props.value,
33
+ }),
34
+ resources: ({ injector, updateState }) => [
35
+ injector.getInstance(ThemeProviderService).theme.subscribe((theme) => updateState({ theme })),
36
+ ],
37
+ render: ({ props, element, injector, getState, updateState }) => {
24
38
  const themeProvider = injector.getInstance(ThemeProviderService)
25
- const theme = themeProvider.theme.getValue()
39
+ const { theme, value } = getState()
26
40
  const { palette } = theme
27
41
 
28
42
  return (
@@ -55,12 +69,14 @@ export const Input = Shade<TextInputProps>({
55
69
  ...props.style,
56
70
  }}
57
71
  >
58
- {props.value}
72
+ {value}
59
73
  </div>
60
74
  ) : (
61
75
  <input
62
76
  onchange={(ev) => {
63
- props.onTextChange && props.onTextChange((ev.target as any).value)
77
+ const newValue = (ev.target as HTMLInputElement).value
78
+ updateState({ value: newValue }, true)
79
+ props.onTextChange && props.onTextChange(newValue)
64
80
  props.onchange && (props.onchange as any)(ev)
65
81
  }}
66
82
  onfocus={() => {
@@ -123,6 +139,7 @@ export const Input = Shade<TextInputProps>({
123
139
  padding: '0.6em 0',
124
140
  ...props.style,
125
141
  }}
142
+ value={value}
126
143
  />
127
144
  )}
128
145
  </label>
@@ -24,7 +24,7 @@ export const Paper = Shade<PartialElement<HTMLDivElement> & { elevation?: 1 | 2
24
24
  color: themeProvider.theme.getValue().text.secondary,
25
25
  margin: '8px',
26
26
  padding: '6px 16px',
27
- ...(props ? props.style : {}),
27
+ ...props?.style,
28
28
  }}
29
29
  >
30
30
  {children}