ViewObject.php.instancesDropdown.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. // @require var JS_GLOBAL_FUNCTION_NAME
  2. // @require var INITIAL_DROPDOWN_DATA
  3. // @require var NAMESPACE
  4. // @require var SET_INSTANCE_URL
  5. if (!JS_GLOBAL_FUNCTION_NAME) throw "Missing JS_GLOBAL_FUNCTION_NAME!";
  6. if (!NAMESPACE) throw "Missing NAMESPACE!";
  7. if (!SET_INSTANCE_URL) throw "Missing SET_INSTANCE_URL!";
  8. if (!DBG) var DBG = false
  9. if (!INITIAL_DROPDOWN_DATA) throw "Missing INITIAL_DROPDOWN_DATA!";
  10. if (!INITIAL_DROPDOWN_DATA.allowed_instances) throw "Missing INITIAL_DROPDOWN_DATA.allowed_instances!";
  11. if (!INITIAL_DROPDOWN_DATA.items) throw "Missing INITIAL_DROPDOWN_DATA.items!";
  12. var instances = {}
  13. var getDropdown = function (pk) {
  14. if(DBG)console.log('>>> getDropdown('+pk+')...')
  15. if (!instances[pk]) instances[pk] = buildDropdown(pk)
  16. return instances[pk]
  17. }
  18. var dropdown = function(pk) {
  19. if(DBG)console.log('>>> dropdown('+pk+')...')
  20. return getDropdown(pk)
  21. }
  22. var buildDropdown = function(pk) {
  23. if (!window.p5VendorJs.React) throw 'Missing p5VendorJs.React'
  24. if (!window.p5VendorJs.ReactDOM) throw 'Missing p5VendorJs.ReactDOM'
  25. if (!window.p5VendorJs.Redux) throw 'Missing p5VendorJs.Redux'
  26. if (!window.p5VendorJs.ReduxThunk) throw 'Missing p5VendorJs.ReduxThunk'
  27. if (!window.fetch) throw 'Missing window.fetch'
  28. var React = window.p5VendorJs.React
  29. var ReactDOM = window.p5VendorJs.ReactDOM
  30. var h = React.createElement
  31. var createStore = window.p5VendorJs.Redux.createStore
  32. var applyMiddleware = window.p5VendorJs.Redux.applyMiddleware
  33. var ReduxThunk = window.p5VendorJs.ReduxThunk
  34. if(DBG)console.log('>>> buildDropdown('+pk+')... ', { INITIAL_DROPDOWN_DATA: INITIAL_DROPDOWN_DATA })
  35. var _btnNode = null
  36. var _dropdownNode = null
  37. var _reducer_instance_set_send = function (payload) {
  38. if(DBG)console.log('RX:_reducer_instance_set_send() payload', payload)
  39. return function (dispatch, getState) { // ReduxThunk
  40. if(DBG)console.log('RX:_reducer_instance_set_send() thunk() payload', payload)
  41. if(DBG)console.log('RX:_reducer_instance_set_send() thunk() getState().item.pk', getState().item.pk)
  42. var instanceToSet = payload
  43. dispatch({ type: 'instance_set_request', payload: instanceToSet })
  44. // return window.fetch(SET_INSTANCE_URL, {
  45. window.fetch(SET_INSTANCE_URL, {
  46. method: 'POST',
  47. headers: { 'Content-Type': 'application/json' },
  48. credentials: 'same-origin',
  49. body: JSON.stringify({
  50. namespace: NAMESPACE,
  51. primaryKey: getState().item.pk,
  52. instance: payload,
  53. toConnect: 'yes',
  54. })
  55. }).then(function (response) {
  56. return response.json()
  57. }).then(function (response) {
  58. dispatch({ type: 'instance_set_receive', payload: { instance: instanceToSet, response: response } })
  59. // }).catch(function (e) {
  60. // dispatch({ type: 'instance_set_error', payload: e })
  61. })
  62. if(DBG)console.log('RX:_reducer_instance_set_send() thunk() END payload', payload)
  63. }
  64. }
  65. var _reducer_instance_set_request = function (state, payload) {
  66. if(DBG)console.log('RX:_reducer_instance_set_request() payload', payload)
  67. state.waitingSet.push(payload)
  68. return state
  69. }
  70. var _reducer_instance_set_receive = function (state, payload) {
  71. if(DBG)console.log('RX:_reducer_instance_set_receive() payload', payload)
  72. // payload: { instance: instanceToSet, response: response }
  73. p5UI__notifyAjaxCallback(payload.response)
  74. // TODO: update whole list in state by response => render list
  75. state.item.instances.push(payload.instance)
  76. state.waitingSet = state.waitingSet.filter(function (inst) {
  77. return inst !== payload.instance
  78. })
  79. return state
  80. }
  81. var _reducer_instance_set_error = function (state, payload) {
  82. if(DBG)console.log('RX:_reducer_instance_set_error() payload', payload)
  83. if(DBG)console.log('DBG network error for set namespace:', payload)
  84. }
  85. var _reducer_instance_unset_send = function (payload) {
  86. if(DBG)console.log('RX:_reducer_instance_unset_send() payload', payload)
  87. return function (dispatch, getState) { // ReduxThunk
  88. if(DBG)console.log('RX:_reducer_instance_unset_send() thunk() payload', payload)
  89. if(DBG)console.log('RX:_reducer_instance_unset_send() thunk() getState().item.pk', getState().item.pk)
  90. var instanceToUnset = payload
  91. dispatch({ type: 'instance_unset_request', payload: instanceToUnset })
  92. window.fetch(SET_INSTANCE_URL, {
  93. method: 'POST',
  94. headers: { 'Content-Type': 'application/json' },
  95. credentials: 'same-origin',
  96. body: JSON.stringify({
  97. namespace: NAMESPACE,
  98. primaryKey: getState().item.pk,
  99. instance: payload,
  100. toConnect: 'no',
  101. })
  102. }).then(function (response) {
  103. return response.json()
  104. }).then(function (response) {
  105. dispatch({ type: 'instance_unset_receive', payload: { instance: instanceToUnset, response: response } })
  106. // }).catch(function (e) {
  107. // dispatch({ type: 'instance_set_error', payload: e })
  108. })
  109. if(DBG)console.log('RX:_reducer_instance_unset_send() thunk() END payload', payload)
  110. }
  111. }
  112. var _reducer_instance_unset_request = function (state, payload) {
  113. if(DBG)console.log('RX:_reducer_instance_unset_request() payload', payload)
  114. state.waitingUnSet.push(payload)
  115. return state
  116. }
  117. var _reducer_instance_unset_receive = function (state, payload) {
  118. if(DBG)console.log('RX:_reducer_instance_unset_receive() payload', payload)
  119. // payload: { instance: instanceToSet, response: response }
  120. p5UI__notifyAjaxCallback(payload.response)
  121. // TODO: update whole list in state by response => render list
  122. state.item.instances = state.item.instances.filter(function (inst) {
  123. return inst !== payload.instance
  124. })
  125. state.waitingUnSet = state.waitingUnSet.filter(function (inst) {
  126. return inst !== payload.instance
  127. })
  128. return state
  129. }
  130. var _reducer_instance_unset_error = function (state, payload) {
  131. if(DBG)console.log('RX:_reducer_instance_unset_error() payload', payload)
  132. if(DBG)console.log('DBG network error for set namespace:', payload)
  133. }
  134. var dropdown = function (state, action) {
  135. if(DBG)console.log('RX:dropdown()', {type: action.type, payload: action.payload, state: state})
  136. switch (action.type) {
  137. case 'INIT': return action.payload
  138. case 'instance_set_request': return _reducer_instance_set_request(state, action.payload)
  139. case 'instance_set_receive': return _reducer_instance_set_receive(state, action.payload)
  140. case 'instance_set_error': return _reducer_instance_set_error(state, action.payload)
  141. case 'instance_unset_request': return _reducer_instance_unset_request(state, action.payload)
  142. case 'instance_unset_receive': return _reducer_instance_unset_receive(state, action.payload)
  143. case 'instance_unset_error': return _reducer_instance_unset_error(state, action.payload)
  144. case 'filter': {
  145. var filtrInstance = action.payload
  146. state.query = filtrInstance
  147. state.filter = state.allowed_instances.filter(function (inst) {
  148. return (-1 !== inst.label.toLowerCase().indexOf(filtrInstance.toLowerCase()))
  149. })
  150. return state
  151. }
  152. default:
  153. return state
  154. }
  155. }
  156. var store = createStore(
  157. dropdown,
  158. applyMiddleware(ReduxThunk)
  159. )
  160. var render = function () {
  161. var state = store.getState()
  162. if(DBG)console.log('RX:render()...')
  163. if (!_dropdownNode) return // throw 'Missing dropdownNode'
  164. if (!_btnNode) return // throw 'Missing btnNode'
  165. ReactDOM.render(
  166. h('div', {}, [
  167. h('div', {
  168. style: { backgroundColor: '#f7f7f7', padding: '8px 12px', borderBottom: '1px solid #ddd' },
  169. }, [
  170. h('div', { className: 'form-group', style: { padding: 0, margin: 0 } }, [
  171. h('div', { className: 'input-group', style: { padding: 0, margin: 0 } }, [
  172. h('input', { type: 'text',
  173. placeholder: 'Szukaj...',
  174. className: 'p5UI__dropdown-input form-control input-sm',
  175. onChange: function (e) { store.dispatch({ type: 'filter', payload: e.target.value }); },
  176. autoFocus: true,
  177. value: state.query,
  178. }),
  179. h('span', { className: 'input-group-addon', style: { padding: '6px', backgroundColor: '#fff' } }, [
  180. h('span', { className: 'glyphicon glyphicon-remove hover-gray', style: { cursor: 'pointer' },
  181. title: "wyczyść",
  182. onClick: function (e) { store.dispatch({ type: 'filter', payload: '' }); },
  183. })
  184. ])
  185. ])
  186. ])
  187. ]),
  188. h('div', { style: { backgroundColor: '#fff', padding: '0' } },
  189. state.filter.map(function (inst) {
  190. return h('label', { className: 'p5UI__dropdown-item', style: {padding: '8px 12px'} }, [
  191. _renderReactToggleInstance({ namespace: inst.namespace, store: store }),
  192. inst.label,
  193. ])
  194. })
  195. ),
  196. ]),
  197. _dropdownNode
  198. );
  199. setTimeout(function () {
  200. if (!_dropdownNode) return false
  201. var searchNode = _dropdownNode.getElementsByTagName('input')[0]
  202. if (!searchNode) return false
  203. searchNode.focus()
  204. }, 100)
  205. }
  206. var _renderReactToggleInstance = function (props) {
  207. var state = props.store.getState()
  208. if (-1 === state.item.instances.indexOf(props.namespace)) {
  209. if (-1 === state.waitingSet.indexOf(props.namespace)) {
  210. return h('input', { type: "checkbox", style: { margin: '0 6px 0 0' },
  211. onClick: function () { props.store.dispatch(_reducer_instance_set_send(props.namespace)); }
  212. })
  213. } else {
  214. return h('input', { type: "checkbox", checked: "checked", disabled: "disabled", style: { margin: '0 6px 0 0' } })
  215. }
  216. } else {
  217. if (-1 === state.waitingUnSet.indexOf(props.namespace)) {
  218. return h('input', { type: "checkbox", checked: "checked", style: { margin: '0 6px 0 0' },
  219. onClick: function () { props.store.dispatch(_reducer_instance_unset_send(props.namespace)); }
  220. })
  221. } else {
  222. return h('input', { type: "checkbox", style: { margin: '0 6px 0 0' } })
  223. }
  224. }
  225. }
  226. store.subscribe(render)
  227. store.dispatch({
  228. type: 'INIT',
  229. payload: {
  230. allowed_instances: INITIAL_DROPDOWN_DATA.allowed_instances,
  231. query: '',
  232. filter: INITIAL_DROPDOWN_DATA.allowed_instances,
  233. item: INITIAL_DROPDOWN_DATA.items.filter(function (data) {
  234. return data['pk'] == pk
  235. }).pop(),
  236. waitingSet: [],
  237. waitingUnSet: [],
  238. }
  239. })
  240. // render(); // shows the initial state - send INIT action
  241. var _state = {
  242. allowed_instances: INITIAL_DROPDOWN_DATA.allowed_instances,
  243. query: '',
  244. filter: INITIAL_DROPDOWN_DATA.allowed_instances,
  245. item: INITIAL_DROPDOWN_DATA.items.filter(function (data) { return data['pk'] == pk; }).pop(),
  246. waitingSet: [],
  247. waitingUnSet: [],
  248. }
  249. return function (btnNode, dropdownNode) {
  250. _btnNode = btnNode
  251. _dropdownNode = dropdownNode
  252. if(DBG)console.log('### F.'+JS_GLOBAL_FUNCTION_NAME, { pk: pk, INITIAL_DROPDOWN_DATA: INITIAL_DROPDOWN_DATA, btnNode: btnNode, dropdownNode: dropdownNode, state: _state }) // TODO: rm globals
  253. render()
  254. }
  255. }
  256. global[JS_GLOBAL_FUNCTION_NAME] = dropdown