@furystack/shades-common-components 3.6.1 → 4.0.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.
Files changed (149) hide show
  1. package/dist/components/app-bar-link.js +18 -20
  2. package/dist/components/app-bar-link.js.map +1 -1
  3. package/dist/components/app-bar.js +7 -6
  4. package/dist/components/app-bar.js.map +1 -1
  5. package/dist/components/avatar.js +20 -18
  6. package/dist/components/avatar.js.map +1 -1
  7. package/dist/components/button.js +32 -28
  8. package/dist/components/button.js.map +1 -1
  9. package/dist/components/command-palette/command-palette-input.js +24 -29
  10. package/dist/components/command-palette/command-palette-input.js.map +1 -1
  11. package/dist/components/command-palette/command-palette-suggestion-list.js +38 -47
  12. package/dist/components/command-palette/command-palette-suggestion-list.js.map +1 -1
  13. package/dist/components/command-palette/index.js +34 -40
  14. package/dist/components/command-palette/index.js.map +1 -1
  15. package/dist/components/data-grid/body.js +6 -13
  16. package/dist/components/data-grid/body.js.map +1 -1
  17. package/dist/components/data-grid/data-grid-row.js +29 -31
  18. package/dist/components/data-grid/data-grid-row.js.map +1 -1
  19. package/dist/components/data-grid/data-grid.js +4 -8
  20. package/dist/components/data-grid/data-grid.js.map +1 -1
  21. package/dist/components/data-grid/footer.js +8 -13
  22. package/dist/components/data-grid/footer.js.map +1 -1
  23. package/dist/components/data-grid/header.js +19 -18
  24. package/dist/components/data-grid/header.js.map +1 -1
  25. package/dist/components/data-grid/selection-cell.js +9 -23
  26. package/dist/components/data-grid/selection-cell.js.map +1 -1
  27. package/dist/components/fab.js +10 -5
  28. package/dist/components/fab.js.map +1 -1
  29. package/dist/components/grid.js.map +1 -1
  30. package/dist/components/inputs/autocomplete.js +3 -13
  31. package/dist/components/inputs/autocomplete.js.map +1 -1
  32. package/dist/components/inputs/input.js +22 -26
  33. package/dist/components/inputs/input.js.map +1 -1
  34. package/dist/components/inputs/text-area.js +2 -6
  35. package/dist/components/inputs/text-area.js.map +1 -1
  36. package/dist/components/loader.js +9 -5
  37. package/dist/components/loader.js.map +1 -1
  38. package/dist/components/modal.js +7 -17
  39. package/dist/components/modal.js.map +1 -1
  40. package/dist/components/noty-list.js +26 -28
  41. package/dist/components/noty-list.js.map +1 -1
  42. package/dist/components/paper.js +11 -5
  43. package/dist/components/paper.js.map +1 -1
  44. package/dist/components/suggest/index.js +14 -20
  45. package/dist/components/suggest/index.js.map +1 -1
  46. package/dist/components/suggest/suggest-input.js +9 -10
  47. package/dist/components/suggest/suggest-input.js.map +1 -1
  48. package/dist/components/suggest/suggest-manager.js +8 -2
  49. package/dist/components/suggest/suggest-manager.js.map +1 -1
  50. package/dist/components/suggest/suggestion-list.js +27 -30
  51. package/dist/components/suggest/suggestion-list.js.map +1 -1
  52. package/dist/components/tabs.js +32 -36
  53. package/dist/components/tabs.js.map +1 -1
  54. package/dist/components/wizard/index.js +4 -7
  55. package/dist/components/wizard/index.js.map +1 -1
  56. package/dist/services/collection-service.js +68 -56
  57. package/dist/services/collection-service.js.map +1 -1
  58. package/package.json +6 -6
  59. package/src/components/app-bar-link.tsx +27 -30
  60. package/src/components/app-bar.tsx +21 -26
  61. package/src/components/avatar.tsx +40 -36
  62. package/src/components/button.tsx +85 -88
  63. package/src/components/command-palette/command-palette-input.tsx +15 -13
  64. package/src/components/command-palette/command-palette-suggestion-list.tsx +50 -58
  65. package/src/components/command-palette/index.tsx +54 -52
  66. package/src/components/data-grid/body.tsx +10 -22
  67. package/src/components/data-grid/data-grid-row.tsx +64 -63
  68. package/src/components/data-grid/data-grid.tsx +11 -11
  69. package/src/components/data-grid/footer.tsx +13 -16
  70. package/src/components/data-grid/header.tsx +34 -30
  71. package/src/components/data-grid/selection-cell.tsx +9 -19
  72. package/src/components/fab.tsx +21 -23
  73. package/src/components/grid.tsx +1 -1
  74. package/src/components/inputs/autocomplete.tsx +10 -16
  75. package/src/components/inputs/input.tsx +27 -27
  76. package/src/components/inputs/text-area.tsx +3 -11
  77. package/src/components/loader.tsx +20 -5
  78. package/src/components/modal.tsx +9 -17
  79. package/src/components/noty-list.tsx +54 -53
  80. package/src/components/paper.tsx +18 -17
  81. package/src/components/suggest/index.tsx +19 -30
  82. package/src/components/suggest/suggest-input.tsx +22 -15
  83. package/src/components/suggest/suggest-manager.ts +15 -3
  84. package/src/components/suggest/suggestion-list.tsx +90 -85
  85. package/src/components/tabs.tsx +50 -62
  86. package/src/components/wizard/index.tsx +6 -13
  87. package/src/services/collection-service.ts +79 -63
  88. package/tsconfig.json +1 -1
  89. package/tsconfig.tsbuildinfo +1 -1
  90. package/types/components/app-bar-link.d.ts +1 -1
  91. package/types/components/app-bar-link.d.ts.map +1 -1
  92. package/types/components/app-bar.d.ts +1 -1
  93. package/types/components/app-bar.d.ts.map +1 -1
  94. package/types/components/avatar.d.ts +2 -1
  95. package/types/components/avatar.d.ts.map +1 -1
  96. package/types/components/button.d.ts +1 -1
  97. package/types/components/button.d.ts.map +1 -1
  98. package/types/components/command-palette/command-palette-input.d.ts +1 -1
  99. package/types/components/command-palette/command-palette-input.d.ts.map +1 -1
  100. package/types/components/command-palette/command-palette-suggestion-list.d.ts +1 -1
  101. package/types/components/command-palette/command-palette-suggestion-list.d.ts.map +1 -1
  102. package/types/components/command-palette/index.d.ts +1 -5
  103. package/types/components/command-palette/index.d.ts.map +1 -1
  104. package/types/components/data-grid/body.d.ts +1 -5
  105. package/types/components/data-grid/body.d.ts.map +1 -1
  106. package/types/components/data-grid/data-grid-row.d.ts +1 -5
  107. package/types/components/data-grid/data-grid-row.d.ts.map +1 -1
  108. package/types/components/data-grid/data-grid.d.ts +5 -3
  109. package/types/components/data-grid/data-grid.d.ts.map +1 -1
  110. package/types/components/data-grid/footer.d.ts +1 -1
  111. package/types/components/data-grid/footer.d.ts.map +1 -1
  112. package/types/components/data-grid/header.d.ts +1 -1
  113. package/types/components/data-grid/header.d.ts.map +1 -1
  114. package/types/components/data-grid/selection-cell.d.ts +1 -1
  115. package/types/components/data-grid/selection-cell.d.ts.map +1 -1
  116. package/types/components/fab.d.ts +1 -1
  117. package/types/components/fab.d.ts.map +1 -1
  118. package/types/components/grid.d.ts +1 -1
  119. package/types/components/grid.d.ts.map +1 -1
  120. package/types/components/inputs/autocomplete.d.ts +1 -1
  121. package/types/components/inputs/autocomplete.d.ts.map +1 -1
  122. package/types/components/inputs/input.d.ts +1 -1
  123. package/types/components/inputs/input.d.ts.map +1 -1
  124. package/types/components/inputs/text-area.d.ts +1 -4
  125. package/types/components/inputs/text-area.d.ts.map +1 -1
  126. package/types/components/loader.d.ts +9 -1
  127. package/types/components/loader.d.ts.map +1 -1
  128. package/types/components/modal.d.ts +1 -1
  129. package/types/components/modal.d.ts.map +1 -1
  130. package/types/components/noty-list.d.ts +2 -2
  131. package/types/components/noty-list.d.ts.map +1 -1
  132. package/types/components/paper.d.ts +1 -1
  133. package/types/components/paper.d.ts.map +1 -1
  134. package/types/components/skeleton.d.ts +1 -1
  135. package/types/components/skeleton.d.ts.map +1 -1
  136. package/types/components/suggest/index.d.ts +1 -5
  137. package/types/components/suggest/index.d.ts.map +1 -1
  138. package/types/components/suggest/suggest-input.d.ts +1 -1
  139. package/types/components/suggest/suggest-input.d.ts.map +1 -1
  140. package/types/components/suggest/suggest-manager.d.ts +2 -1
  141. package/types/components/suggest/suggest-manager.d.ts.map +1 -1
  142. package/types/components/suggest/suggestion-list.d.ts +1 -1
  143. package/types/components/suggest/suggestion-list.d.ts.map +1 -1
  144. package/types/components/tabs.d.ts +2 -2
  145. package/types/components/tabs.d.ts.map +1 -1
  146. package/types/components/wizard/index.d.ts +2 -2
  147. package/types/components/wizard/index.d.ts.map +1 -1
  148. package/types/services/collection-service.d.ts +4 -0
  149. package/types/services/collection-service.d.ts.map +1 -1
@@ -1,30 +1,28 @@
1
1
  import type { PartialElement } from '@furystack/shades'
2
+ import { attachProps } from '@furystack/shades'
2
3
  import { Shade, createComponent } from '@furystack/shades'
3
4
 
4
5
  export const Fab = Shade<PartialElement<HTMLDivElement>>({
5
6
  shadowDomName: 'shade-fab',
6
- render: ({ props, children }) => {
7
- return (
8
- <div
9
- {...props}
10
- style={{
11
- position: 'fixed',
12
- bottom: '32px',
13
- right: '32px',
14
- background: 'gray',
15
- width: '64px',
16
- height: '64px',
17
- display: 'flex',
18
- justifyContent: 'center',
19
- alignItems: 'center',
20
- borderRadius: '50%',
21
- boxShadow: '2px 2px 4px rgba(0,0,0,0.3)',
22
- cursor: 'pointer',
23
- ...props.style,
24
- }}
25
- >
26
- {children}
27
- </div>
28
- )
7
+ render: ({ props, children, element }) => {
8
+ attachProps(element, {
9
+ ...props,
10
+ style: {
11
+ position: 'fixed',
12
+ bottom: '32px',
13
+ right: '32px',
14
+ background: 'gray',
15
+ width: '64px',
16
+ height: '64px',
17
+ display: 'flex',
18
+ justifyContent: 'center',
19
+ alignItems: 'center',
20
+ borderRadius: '50%',
21
+ boxShadow: '2px 2px 4px rgba(0,0,0,0.3)',
22
+ cursor: 'pointer',
23
+ ...props?.style,
24
+ },
25
+ })
26
+ return <>{children}</>
29
27
  },
30
28
  })
@@ -23,7 +23,7 @@ export type RowCells<T> = {
23
23
  [TKey in keyof T | 'default']?: (element: T) => JSX.Element
24
24
  }
25
25
 
26
- export const Grid: <T>(props: GridProps<T>, children: ChildrenList) => JSX.Element<any, any> = Shade({
26
+ export const Grid: <T>(props: GridProps<T>, children: ChildrenList) => JSX.Element<any> = Shade({
27
27
  shadowDomName: 'shade-grid',
28
28
  render: ({ props, injector }) => {
29
29
  const { theme } = injector.getInstance(ThemeProviderService)
@@ -2,27 +2,21 @@ import { Shade, createComponent } from '@furystack/shades'
2
2
  import type { TextInputProps } from './input'
3
3
  import { Input } from './input'
4
4
 
5
- export const Autocomplete = Shade<
6
- { inputProps?: TextInputProps; suggestions: string[]; strict?: boolean; onchange?: (value: string) => void },
7
- { dataListId: string }
8
- >({
9
- getInitialState: () => ({
10
- dataListId: (Math.random() + 1).toString(36).substring(3),
11
- }),
12
- constructed: ({ getState, element }) => {
13
- const { dataListId } = getState()
14
- const input = element.querySelector('input')
15
- if (input) {
16
- input.setAttribute('list', dataListId)
17
- }
18
- },
5
+ export const Autocomplete = Shade<{
6
+ inputProps?: TextInputProps
7
+ suggestions: string[]
8
+ strict?: boolean
9
+ onchange?: (value: string) => void
10
+ }>({
19
11
  shadowDomName: 'shade-autocomplete',
20
- render: ({ props, getState }) => {
21
- const { dataListId } = getState()
12
+ render: ({ props, useState }) => {
13
+ const [dataListId] = useState('dataListId', (Math.random() + 1).toString(36).substring(3))
14
+
22
15
  return (
23
16
  <div>
24
17
  <Input
25
18
  {...props.inputProps}
19
+ list={dataListId as any}
26
20
  onchange={(ev) => {
27
21
  const { value } = ev.target as any
28
22
  if (props.strict) {
@@ -1,5 +1,6 @@
1
1
  import type { PartialElement } from '@furystack/shades'
2
2
  import { Shade, createComponent, attachStyles } from '@furystack/shades'
3
+ import { ObservableValue } from '@furystack/utils'
3
4
  import { ThemeProviderService } from '../..'
4
5
  import type { Palette } from '../../services'
5
6
 
@@ -106,33 +107,32 @@ const getLabelStyle = ({
106
107
  }
107
108
  }
108
109
 
109
- export const Input = Shade<TextInputProps, TextInputState>({
110
+ export const Input = Shade<TextInputProps>({
110
111
  shadowDomName: 'shade-input',
111
- getInitialState: ({ props }) => ({
112
- value: props.value || '',
113
- focused: props.autofocus || false,
114
- validity: { valid: true } as ValidityState,
115
- }),
116
- compareState: ({ newState, element, props, injector }) => {
112
+ render: ({ props, injector, useObservable, element }) => {
117
113
  const themeProvider = injector.getInstance(ThemeProviderService)
118
- const label = element.querySelector('label') as HTMLLabelElement
119
- attachStyles(label, { style: getLabelStyle({ themeProvider, props, state: newState }) })
120
114
 
121
- const helper = element.querySelector<HTMLSpanElement>('span.helperText')
122
- const helperNode = props.getHelperText?.({ state: newState }) || ''
123
- helper?.replaceChildren(helperNode)
115
+ const [state, setState] = useObservable<TextInputState>(
116
+ 'inputState',
117
+ new ObservableValue({
118
+ value: props.value || '',
119
+ focused: props.autofocus || false,
120
+ validity: { valid: true } as ValidityState,
121
+ }),
122
+ (newState) => {
123
+ const label = element.querySelector('label') as HTMLLabelElement
124
+ attachStyles(label, { style: getLabelStyle({ themeProvider, props, state: newState }) })
124
125
 
125
- const startIcon = element.querySelector<HTMLSpanElement>('span.startIcon')
126
- startIcon?.replaceChildren(props.getStartIcon?.({ state: newState }) || '')
127
- const endIcon = element.querySelector<HTMLSpanElement>('span.endIcon')
128
- endIcon?.replaceChildren(props.getEndIcon?.({ state: newState }) || '')
126
+ const helper = element.querySelector<HTMLSpanElement>('span.helperText')
127
+ const helperNode = props.getHelperText?.({ state: newState }) || ''
128
+ helper?.replaceChildren(helperNode)
129
129
 
130
- return false
131
- },
132
- render: ({ props, getState, updateState, injector }) => {
133
- const state = getState()
134
- const { value } = state
135
- const themeProvider = injector.getInstance(ThemeProviderService)
130
+ const startIcon = element.querySelector<HTMLSpanElement>('span.startIcon')
131
+ startIcon?.replaceChildren(props.getStartIcon?.({ state: newState }) || '')
132
+ const endIcon = element.querySelector<HTMLSpanElement>('span.endIcon')
133
+ endIcon?.replaceChildren(props.getEndIcon?.({ state: newState }) || '')
134
+ },
135
+ )
136
136
 
137
137
  return (
138
138
  <label {...props.labelProps} style={getLabelStyle({ props, state, themeProvider })}>
@@ -150,20 +150,20 @@ export const Input = Shade<TextInputProps, TextInputState>({
150
150
  oninvalid={(ev) => {
151
151
  ev.preventDefault()
152
152
  const el = ev.target as HTMLInputElement
153
- updateState({ validity: el.validity })
153
+ setState({ ...state, validity: el.validity })
154
154
  }}
155
155
  onchange={(ev) => {
156
156
  const el = ev.target as HTMLInputElement
157
157
  const newValue = el.value
158
- updateState({ value: newValue, validity: el?.validity })
158
+ setState({ ...state, value: newValue, validity: el?.validity })
159
159
  props.onTextChange?.(newValue)
160
160
  props.onchange && (props.onchange as any)(ev)
161
161
  }}
162
162
  onfocus={() => {
163
- updateState({ focused: true })
163
+ setState({ ...state, focused: true })
164
164
  }}
165
165
  onblur={() => {
166
- updateState({ focused: false })
166
+ setState({ ...state, focused: false })
167
167
  }}
168
168
  {...props}
169
169
  style={{
@@ -180,7 +180,7 @@ export const Input = Shade<TextInputProps, TextInputState>({
180
180
  flexGrow: '1',
181
181
  ...props.style,
182
182
  }}
183
- value={value}
183
+ value={state.value}
184
184
  />
185
185
  {props.getEndIcon ? <span className="endIcon">{props.getEndIcon({ state })}</span> : null}
186
186
  </div>
@@ -10,19 +10,11 @@ export interface TextAreaProps extends PartialElement<HTMLTextAreaElement> {
10
10
  variant?: 'contained' | 'outlined'
11
11
  }
12
12
 
13
- export type TextAreaInputState = {
14
- value?: string
15
- }
16
-
17
- export const TextArea = Shade<TextAreaProps, TextAreaInputState>({
13
+ export const TextArea = Shade<TextAreaProps>({
18
14
  shadowDomName: 'shade-text-area',
19
- getInitialState: ({ props }) => ({
20
- value: props.value,
21
- }),
22
- render: ({ props, element, injector, getState }) => {
15
+ render: ({ props, element, injector }) => {
23
16
  const themeProvider = injector.getInstance(ThemeProviderService)
24
17
  const { theme } = themeProvider
25
- const { value } = getState()
26
18
  const { palette } = theme
27
19
 
28
20
  return (
@@ -105,7 +97,7 @@ export const TextArea = Shade<TextAreaProps, TextAreaInputState>({
105
97
  }
106
98
  }}
107
99
  >
108
- {value}
100
+ {props.value}
109
101
  </div>
110
102
  </label>
111
103
  )
@@ -1,4 +1,5 @@
1
1
  import { Shade, createComponent } from '@furystack/shades'
2
+ import { ThemeProviderService } from '../services'
2
3
  import { promisifyAnimation } from '../utils'
3
4
 
4
5
  interface LoaderProps {
@@ -10,15 +11,29 @@ interface LoaderProps {
10
11
  * The time to wait before the loader shows up
11
12
  */
12
13
  delay?: number
14
+
15
+ /**
16
+ * The color of the loader
17
+ */
18
+ borderColor?: string
19
+
20
+ /**
21
+ * The width of the border
22
+ */
23
+ borderWidth?: number
13
24
  }
14
25
 
15
26
  export const Loader = Shade<LoaderProps>({
16
27
  shadowDomName: 'shade-loader',
17
- render: ({ element, props }) => {
28
+ render: ({ element, props, injector }) => {
29
+ const { theme } = injector.getInstance(ThemeProviderService)
30
+
18
31
  element.style.display = 'inline-block'
19
32
  element.style.transformOrigin = 'center'
20
33
  element.style.opacity = '0'
21
34
  const { delay = 500 } = props
35
+ const { borderWidth = 15 } = props
36
+ const { borderColor = theme.palette.primary.main } = props
22
37
 
23
38
  setTimeout(() => {
24
39
  promisifyAnimation(element, [{ opacity: '0' }, { opacity: '1' }], {
@@ -40,10 +55,10 @@ export const Loader = Shade<LoaderProps>({
40
55
  <div
41
56
  style={{
42
57
  position: 'relative',
43
- width: 'calc(100% - 30px)',
44
- height: 'calc(100% - 30px)',
45
- border: '15px solid rgba(128,128,128,0.1)',
46
- borderBottom: '15px solid red',
58
+ width: `calc(100% - ${borderWidth * 2}px)`,
59
+ height: `calc(100% - ${borderWidth * 2}px)`,
60
+ border: `${borderWidth}px solid rgba(128,128,128,0.1)`,
61
+ borderBottom: `${borderWidth}px solid ${borderColor}`,
47
62
  borderRadius: '50%',
48
63
  }}
49
64
  />
@@ -9,28 +9,20 @@ export type ModalProps = {
9
9
  hideAnimation?: (el: Element | null) => Promise<unknown>
10
10
  }
11
11
 
12
- export const Modal = Shade<ModalProps, { isVisible?: boolean }>({
13
- getInitialState: ({ props }) => ({ isVisible: props.isVisible.getValue() }),
12
+ export const Modal = Shade<ModalProps>({
14
13
  shadowDomName: 'shade-modal',
15
- resources: ({ props, element, updateState }) => [
16
- props.isVisible.subscribe(async (visible) => {
17
- if (visible) {
18
- updateState({ isVisible: visible })
19
- await props.showAnimation?.(element)
20
- } else {
21
- props.hideAnimation
22
- ? await props.hideAnimation?.(element).finally(() => updateState({ isVisible: visible }))
23
- : updateState({ isVisible: visible })
24
- }
25
- }),
26
- ],
14
+ render: ({ props, children, useObservable, element }) => {
15
+ const [isVisible] = useObservable('isVisible', props.isVisible)
16
+
17
+ if (isVisible) {
18
+ props.showAnimation?.(element)
19
+ }
27
20
 
28
- render: ({ props, getState, children }) => {
29
- const { isVisible } = getState()
30
21
  return isVisible ? (
31
22
  <div
32
23
  className="shade-backdrop"
33
- onclick={() => {
24
+ onclick={async () => {
25
+ await props.hideAnimation?.(element)
34
26
  props.onClose?.()
35
27
  }}
36
28
  style={{
@@ -1,4 +1,4 @@
1
- import { createComponent, Shade } from '@furystack/shades'
1
+ import { attachProps, createComponent, Shade } from '@furystack/shades'
2
2
  import type { NotyModel } from '../services/noty-service'
3
3
  import { NotyService } from '../services/noty-service'
4
4
  import { ThemeProviderService } from '../services/theme-provider-service'
@@ -24,10 +24,9 @@ export const NotyComponent = Shade<{ model: NotyModel; onDismiss: () => void }>(
24
24
  shadowDomName: 'shade-noty',
25
25
  constructed: ({ element }) => {
26
26
  setTimeout(() => {
27
- const container = element.querySelector('div')
28
- const height = container?.scrollHeight || 80
27
+ const height = element.scrollHeight || 80
29
28
  promisifyAnimation(
30
- container,
29
+ element,
31
30
  [
32
31
  { opacity: '0', height: '0px' },
33
32
  { opacity: '1', height: `${height}px` },
@@ -47,11 +46,10 @@ export const NotyComponent = Shade<{ model: NotyModel; onDismiss: () => void }>(
47
46
  const textColor = themeProvider.getTextColor(colors.main)
48
47
 
49
48
  const removeSelf = async () => {
50
- const container = element.querySelector('div')
51
49
  await promisifyAnimation(
52
- container,
50
+ element,
53
51
  [
54
- { opacity: '1', height: `${container?.scrollHeight || 0}px`, margin: '8px 8px' },
52
+ { opacity: '1', height: `${element?.scrollHeight || 0}px`, margin: '8px 8px' },
55
53
  { opacity: '0', height: '0px', margin: '0px 8px' },
56
54
  ],
57
55
  {
@@ -68,22 +66,24 @@ export const NotyComponent = Shade<{ model: NotyModel; onDismiss: () => void }>(
68
66
  setTimeout(removeSelf, timeout)
69
67
  }
70
68
 
69
+ attachProps(element, {
70
+ className: `noty ${props.model.type}`,
71
+ style: {
72
+ width: '300px',
73
+ display: 'flex',
74
+ flexDirection: 'column',
75
+ height: '0px',
76
+ backgroundColor: colors.main,
77
+ color: textColor,
78
+ margin: '8px',
79
+ overflow: 'hidden',
80
+ borderRadius: '6px',
81
+ boxShadow: '1px 3px 6px rgba(0,0,0,0.3)',
82
+ },
83
+ })
84
+
71
85
  return (
72
- <div
73
- className={`noty ${props.model.type}`}
74
- style={{
75
- width: '300px',
76
- display: 'flex',
77
- flexDirection: 'column',
78
- height: '0px',
79
- backgroundColor: colors.main,
80
- color: textColor,
81
- margin: '8px',
82
- overflow: 'hidden',
83
- borderRadius: '6px',
84
- boxShadow: '1px 3px 6px rgba(0,0,0,0.3)',
85
- }}
86
- >
86
+ <>
87
87
  <div
88
88
  style={{
89
89
  display: 'flex',
@@ -119,45 +119,46 @@ export const NotyComponent = Shade<{ model: NotyModel; onDismiss: () => void }>(
119
119
  </Button>
120
120
  </div>
121
121
  <div style={{ padding: '16px 16px' }}>{props.model.body}</div>
122
- </div>
122
+ </>
123
123
  )
124
124
  },
125
125
  })
126
126
 
127
- export const NotyList = Shade<unknown, { currentNotys: NotyModel[] }>({
128
- getInitialState: ({ injector }) => ({ currentNotys: injector.getInstance(NotyService).notys.getValue() }),
129
- constructed: ({ injector, element }) => {
130
- const notyService = injector.getInstance(NotyService)
131
- const observables = [
132
- notyService.onNotyAdded.subscribe((n) => {
133
- element.querySelector('div')?.append(<NotyComponent model={n} onDismiss={() => notyService.removeNoty(n)} />)
134
- }),
135
- notyService.onNotyRemoved.subscribe((n) => {
136
- element.querySelectorAll('shade-noty').forEach((e) => {
137
- if ((e as JSX.Element).props.model === n) {
138
- e.remove()
139
- }
140
- })
141
- }),
142
- ]
143
- return () => observables.forEach((o) => o.dispose())
144
- },
127
+ export const NotyList = Shade({
145
128
  shadowDomName: 'shade-noty-list',
146
- render: ({ getState, injector }) => {
129
+ render: ({ useObservable, injector, element }) => {
130
+ const notyService = injector.getInstance(NotyService)
131
+
132
+ attachProps(element, {
133
+ style: {
134
+ position: 'fixed',
135
+ bottom: '1em',
136
+ right: '1em',
137
+ display: 'flex',
138
+ flexDirection: 'column',
139
+ },
140
+ })
141
+
142
+ const currentNotys = notyService.notys.getValue()
143
+
144
+ useObservable('addNoty', notyService.onNotyAdded, (n) =>
145
+ element.append(<NotyComponent model={n} onDismiss={() => notyService.removeNoty(n)} />),
146
+ )
147
+
148
+ useObservable('removeNoty', notyService.onNotyRemoved, (n) => {
149
+ element.querySelectorAll('shade-noty').forEach((e) => {
150
+ if ((e as JSX.Element).props.model === n) {
151
+ e.remove()
152
+ }
153
+ })
154
+ })
155
+
147
156
  return (
148
- <div
149
- style={{
150
- position: 'fixed',
151
- bottom: '1em',
152
- right: '1em',
153
- display: 'flex',
154
- flexDirection: 'column',
155
- }}
156
- >
157
- {getState().currentNotys.map((n) => (
157
+ <>
158
+ {currentNotys.map((n) => (
158
159
  <NotyComponent model={n} onDismiss={() => injector.getInstance(NotyService).removeNoty(n)} />
159
160
  ))}
160
- </div>
161
+ </>
161
162
  )
162
163
  },
163
164
  })
@@ -1,27 +1,28 @@
1
1
  import type { PartialElement } from '@furystack/shades'
2
+ import { attachProps } from '@furystack/shades'
2
3
  import { Shade, createComponent } from '@furystack/shades'
3
4
  import { ThemeProviderService } from '../services/theme-provider-service'
4
5
 
5
6
  export const Paper = Shade<PartialElement<HTMLDivElement> & { elevation?: 1 | 2 | 3 }>({
6
7
  shadowDomName: 'shade-paper',
7
8
 
8
- render: ({ injector, props, children }) => {
9
+ render: ({ injector, props, children, element }) => {
9
10
  const themeProvider = injector.getInstance(ThemeProviderService)
10
- return (
11
- <div
12
- {...props}
13
- style={{
14
- borderRadius: '3px',
15
- boxShadow: props.elevation ? `1px ${props.elevation}px ${props.elevation}px rgba(0,0,0,0.3)` : '',
16
- backgroundColor: themeProvider.theme.background.paper,
17
- color: themeProvider.theme.text.secondary,
18
- margin: '8px',
19
- padding: '6px 16px',
20
- ...props?.style,
21
- }}
22
- >
23
- {children}
24
- </div>
25
- )
11
+ const { elevation = 1 } = props
12
+
13
+ attachProps(element, {
14
+ ...props,
15
+ style: {
16
+ borderRadius: '3px',
17
+ boxShadow: elevation ? `1px ${elevation}px ${elevation}px rgba(0,0,0,0.3)` : '',
18
+ backgroundColor: themeProvider.theme.background.paper,
19
+ color: themeProvider.theme.text.secondary,
20
+ margin: '8px',
21
+ padding: '6px 16px',
22
+ ...props?.style,
23
+ },
24
+ })
25
+
26
+ return <>{children}</>
26
27
  },
27
28
  })
@@ -6,6 +6,7 @@ import { SuggestManager } from './suggest-manager'
6
6
  import type { SuggestionResult } from './suggestion-result'
7
7
  import { SuggestInput } from './suggest-input'
8
8
  import { SuggestionList } from './suggestion-list'
9
+ import { ThemeProviderService } from '../../services'
9
10
 
10
11
  export * from './suggest-input'
11
12
  export * from './suggest-manager'
@@ -20,25 +21,19 @@ export interface SuggestProps<T> {
20
21
  style?: Partial<CSSStyleDeclaration>
21
22
  }
22
23
 
23
- export interface SuggestState<T> {
24
- manager: SuggestManager<T>
25
- }
26
-
27
- export const Suggest: <T>(props: SuggestProps<T>, children: ChildrenList) => JSX.Element<any, any> = Shade<
28
- SuggestProps<any>,
29
- SuggestState<any>
24
+ export const Suggest: <T>(props: SuggestProps<T>, children: ChildrenList) => JSX.Element<any> = Shade<
25
+ SuggestProps<any>
30
26
  >({
31
27
  shadowDomName: 'shade-suggest',
32
- getInitialState: ({ props }) => ({
33
- manager: new SuggestManager(props.getEntries, props.getSuggestionEntry),
34
- }),
35
- constructed: ({ element: el, getState, props }) => {
36
- const { manager } = getState()
37
- const element = el.querySelector('.input-container') as HTMLDivElement
38
- manager.element = el
28
+ render: ({ props, injector, element, useDisposable, useObservable }) => {
29
+ element.style.flexGrow = '1'
30
+ const manager = useDisposable('manager', () => new SuggestManager(props.getEntries, props.getSuggestionEntry))
31
+ const { theme } = injector.getInstance(ThemeProviderService)
32
+ const inputContainer = element.querySelector('.input-container') as HTMLDivElement
33
+ manager.element = element
39
34
  manager.isOpened.subscribe((isOpened) => {
40
- const suggestions = el.querySelector('.close-suggestions')
41
- const postControls = el.querySelector('.post-controls')
35
+ const suggestions = element.querySelector('.close-suggestions')
36
+ const postControls = element.querySelector('.post-controls')
42
37
  if (isOpened) {
43
38
  promisifyAnimation(suggestions, [{ opacity: 0 }, { opacity: 1 }], {
44
39
  duration: 500,
@@ -50,7 +45,7 @@ export const Suggest: <T>(props: SuggestProps<T>, children: ChildrenList) => JSX
50
45
  fill: 'forwards',
51
46
  })
52
47
 
53
- promisifyAnimation(element, [{ background: 'transparent' }, { background: 'rgba(128,128,128,0.1)' }], {
48
+ promisifyAnimation(inputContainer, [{ background: 'transparent' }, { background: theme.background.default }], {
54
49
  duration: 500,
55
50
  fill: 'forwards',
56
51
  easing: 'cubic-bezier(0.050, 0.570, 0.840, 1.005)',
@@ -67,7 +62,7 @@ export const Suggest: <T>(props: SuggestProps<T>, children: ChildrenList) => JSX
67
62
  delay: 300,
68
63
  })
69
64
 
70
- promisifyAnimation(element, [{ background: 'rgba(128,128,128,0.1)' }, { background: 'transparent' }], {
65
+ promisifyAnimation(inputContainer, [{ background: theme.background.default }, { background: 'transparent' }], {
71
66
  duration: 300,
72
67
  fill: 'forwards',
73
68
  easing: 'cubic-bezier(0.000, 0.245, 0.190, 0.790)',
@@ -75,7 +70,7 @@ export const Suggest: <T>(props: SuggestProps<T>, children: ChildrenList) => JSX
75
70
  }
76
71
  })
77
72
  manager.isLoading.subscribe(async (isLoading) => {
78
- const loader = el.querySelector('.loader-container')
73
+ const loader = element.querySelector('shade-loader')
79
74
  if (isLoading) {
80
75
  promisifyAnimation(loader, [{ opacity: 0 }, { opacity: 1 }], {
81
76
  duration: 100,
@@ -88,12 +83,7 @@ export const Suggest: <T>(props: SuggestProps<T>, children: ChildrenList) => JSX
88
83
  })
89
84
  }
90
85
  })
91
- manager.onSelectSuggestion.subscribe((value) => props.onSelectSuggestion(value))
92
- return () => manager.dispose()
93
- },
94
- render: ({ props, injector, element, getState }) => {
95
- element.style.flexGrow = '1'
96
- const { manager } = getState()
86
+ useObservable('onSelectSuggestion', manager.onSelectSuggestion, props.onSelectSuggestion)
97
87
  return (
98
88
  <div
99
89
  style={{ display: 'flex', flexDirection: 'column' }}
@@ -153,12 +143,11 @@ export const Suggest: <T>(props: SuggestProps<T>, children: ChildrenList) => JSX
153
143
  overflow: 'hidden',
154
144
  }}
155
145
  >
156
- <div
157
- className="loader-container"
146
+ <Loader
158
147
  style={{ width: '20px', height: '20px', opacity: manager.isLoading.getValue() ? '1' : '0' }}
159
- >
160
- <Loader style={{ width: '100%', height: '100%' }} />
161
- </div>
148
+ delay={0}
149
+ borderWidth={4}
150
+ />
162
151
  <div
163
152
  className="close-suggestions"
164
153
  onclick={() => manager.isOpened.setValue(false)}
@@ -1,27 +1,34 @@
1
1
  import { Shade, createComponent } from '@furystack/shades'
2
+ import { ThemeProviderService } from '../../services'
2
3
  import type { SuggestManager } from './suggest-manager'
3
4
 
4
- export const SuggestInput = Shade<{ manager: SuggestManager<any> }, { isOpened: boolean }>({
5
- getInitialState: ({ props }) => ({ isOpened: props.manager.isOpened.getValue() }),
6
- resources: ({ element, props }) => [
7
- props.manager.isOpened.subscribe(async (isOpened) => {
8
- const input = element.firstChild as HTMLInputElement
9
- if (isOpened) {
10
- input.focus()
11
- } else {
12
- input.value = ''
13
- }
14
- }),
15
- ],
5
+ export const SuggestInput = Shade<{ manager: SuggestManager<any> }>({
16
6
  shadowDomName: 'shades-suggest-input',
17
- render: ({ element }) => {
18
- element.style.width = '100%' //manager.isOpened.getValue() ? '100%' : '0%'
7
+ render: ({ element, props, useObservable, injector }) => {
8
+ const { theme } = injector.getInstance(ThemeProviderService)
9
+
10
+ element.style.width = '100%'
19
11
  element.style.overflow = 'hidden'
12
+
13
+ useObservable(
14
+ 'isOpened',
15
+ props.manager.isOpened,
16
+ (isOpened) => {
17
+ const input = element.firstChild as HTMLInputElement
18
+ if (isOpened) {
19
+ input.focus()
20
+ } else {
21
+ input.value = ''
22
+ }
23
+ },
24
+ true,
25
+ )
26
+
20
27
  return (
21
28
  <input
22
29
  autofocus
23
30
  style={{
24
- color: 'white',
31
+ color: theme.text.primary,
25
32
  outline: 'none',
26
33
  padding: '1em',
27
34
  background: 'transparent',