ViewObject.php.instancesDropdown.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  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', {}, [].concat(
  167. h('input', { type: 'text',
  168. placeholder: 'Szukaj...',
  169. className: 'p5UI__dropdown-input',
  170. onChange: function (e) { store.dispatch({ type: 'filter', payload: e.target.value }); },
  171. autoFocus: true,
  172. value: state.query,
  173. })
  174. ).concat(
  175. state.filter.map(function (inst) {
  176. return h('div', { className: 'p5UI__dropdown-item', style: {padding: '4px 0'} }, [
  177. _renderReactBtnToggleInstance({ namespace: inst.namespace, store: store }),
  178. inst.label,
  179. ])
  180. })
  181. )
  182. ),
  183. _dropdownNode
  184. );
  185. setTimeout(function () {
  186. if (_dropdownNode.firstChild && _dropdownNode.firstChild.firstChild)
  187. _dropdownNode.firstChild.firstChild.focus()
  188. }, 100)
  189. }
  190. var _renderReactBtnToggleInstance = function (props) {
  191. var state = props.store.getState()
  192. if (-1 === state.item.instances.indexOf(props.namespace)) {
  193. if (-1 === state.waitingSet.indexOf(props.namespace)) {
  194. return h('button', { style: { margin: '0 6px 0 0' },
  195. className: 'btn btn-xs btn-default',
  196. onClick: function () { props.store.dispatch(_reducer_instance_set_send(props.namespace)); }
  197. }, 'ustaw')
  198. } else {
  199. return h('button', { style: { margin: '0 6px 0 0' },
  200. className: 'btn btn-xs btn-default disabled',
  201. }, 'ustaw...')
  202. }
  203. } else {
  204. if (-1 === state.waitingUnSet.indexOf(props.namespace)) {
  205. return h('button', { style: { margin: '0 6px 0 0' },
  206. className: 'btn btn-xs btn-default',
  207. onClick: function () { props.store.dispatch(_reducer_instance_unset_send(props.namespace)); }
  208. }, 'usuń')
  209. } else {
  210. return h('button', { style: { margin: '0 6px 0 0' },
  211. className: 'btn btn-xs btn-default disabled',
  212. }, 'usuń...')
  213. }
  214. }
  215. }
  216. store.subscribe(render)
  217. store.dispatch({
  218. type: 'INIT',
  219. payload: {
  220. allowed_instances: INITIAL_DROPDOWN_DATA.allowed_instances,
  221. query: '',
  222. filter: INITIAL_DROPDOWN_DATA.allowed_instances,
  223. item: INITIAL_DROPDOWN_DATA.items.filter(function (data) {
  224. return data['pk'] == pk
  225. }).pop(),
  226. waitingSet: [],
  227. waitingUnSet: [],
  228. }
  229. })
  230. // render(); // shows the initial state - send INIT action
  231. var _state = {
  232. allowed_instances: INITIAL_DROPDOWN_DATA.allowed_instances,
  233. query: '',
  234. filter: INITIAL_DROPDOWN_DATA.allowed_instances,
  235. item: INITIAL_DROPDOWN_DATA.items.filter(function (data) { return data['pk'] == pk; }).pop(),
  236. waitingSet: [],
  237. waitingUnSet: [],
  238. }
  239. return function (btnNode, dropdownNode) {
  240. _btnNode = btnNode
  241. _dropdownNode = dropdownNode
  242. 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
  243. render()
  244. }
  245. }
  246. global[JS_GLOBAL_FUNCTION_NAME] = dropdown