Moka-Views.st 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  1. Smalltalk current createPackage: 'Moka-Views'!
  2. MKSingleAspectView subclass: #MKButtonView
  3. instanceVariableNames: 'default label'
  4. package: 'Moka-Views'!
  5. !MKButtonView commentStamp!
  6. I am a push button view. My default controller is `MKButtonController`.
  7. My controller must answer to `#onPressed`.
  8. ## API
  9. - Instances can be set a `default` button
  10. - Use `#label:` to set the label string!
  11. !MKButtonView methodsFor: 'accessing'!
  12. cssClass
  13. ^ String streamContents: [ :stream |
  14. stream << super cssClass << ' mk_button'.
  15. self isDefault
  16. ifTrue: [ stream << ' default' ] ]
  17. !
  18. default
  19. ^ default
  20. !
  21. default: aBoolean
  22. default := aBoolean
  23. !
  24. label
  25. ^ label ifNil: [ self defaultLabel ]
  26. !
  27. label: aString
  28. label := aString
  29. !
  30. tag
  31. ^ 'button'
  32. ! !
  33. !MKButtonView methodsFor: 'defaults'!
  34. defaultControllerClass
  35. ^ MKButtonController
  36. !
  37. defaultLabel
  38. ^ 'OK'
  39. !
  40. defaultLayout
  41. ^ super defaultLayout
  42. width: 80;
  43. height: 24;
  44. yourself
  45. ! !
  46. !MKButtonView methodsFor: 'rendering'!
  47. renderContentOn: html
  48. html with: self label
  49. ! !
  50. !MKButtonView methodsFor: 'testing'!
  51. isDefault
  52. ^ self default ifNil: [ false ]
  53. ! !
  54. MKSingleAspectView subclass: #MKCheckboxView
  55. instanceVariableNames: 'id'
  56. package: 'Moka-Views'!
  57. !MKCheckboxView commentStamp!
  58. I am a checkbox view. My default controller is `MKCheckboxController`.
  59. My controller must answer to `#onToggled:`.
  60. ##API
  61. - If no `aspect` is provided, the ckeckbox state will always be off.
  62. - use `#label:` to set the label string.!
  63. !MKCheckboxView methodsFor: 'accessing'!
  64. checked
  65. ^ self aspectValue ifNil: [ false ]
  66. !
  67. cssClass
  68. ^ super cssClass, ' mk_checkbox'
  69. !
  70. id
  71. ^ id ifNil: [ id := 1000000 atRandom asString ]
  72. ! !
  73. !MKCheckboxView methodsFor: 'defaults'!
  74. defaultControllerClass
  75. ^ MKCheckboxController
  76. !
  77. defaultLayout
  78. ^ super defaultLayout
  79. width: 16;
  80. height: 16;
  81. yourself
  82. ! !
  83. !MKCheckboxView methodsFor: 'events'!
  84. update
  85. self checked
  86. ifTrue: [ root asJQuery addClass: 'checked' ]
  87. ifFalse: [ root asJQuery removeClass: 'checked' ]
  88. ! !
  89. !MKCheckboxView methodsFor: 'rendering'!
  90. renderContentOn: html
  91. self checked ifTrue: [
  92. root asJQuery addClass: 'checked' ].
  93. root at: 'tabindex' put: '0'
  94. ! !
  95. MKCheckboxView subclass: #MKSwitchView
  96. instanceVariableNames: ''
  97. package: 'Moka-Views'!
  98. !MKSwitchView commentStamp!
  99. I am a switch view, similar to a `MKCheckboxView` but displayed as a switch.
  100. My default controller is `MKCheckboxController`.!
  101. !MKSwitchView methodsFor: 'accessing'!
  102. checkboxCssClass
  103. ^ 'mk_switch'
  104. !
  105. cssClass
  106. ^ super cssClass, ' mk_switch'
  107. ! !
  108. !MKSwitchView methodsFor: 'defaults'!
  109. defaultLayout
  110. ^ super defaultLayout
  111. width: 48;
  112. height: 20;
  113. yourself
  114. ! !
  115. MKView subclass: #MKContainerView
  116. instanceVariableNames: 'childView'
  117. package: 'Moka-Views'!
  118. !MKContainerView commentStamp!
  119. I display my single `childView`.
  120. I am used to switch between views.!
  121. !MKContainerView methodsFor: 'accessing'!
  122. childView
  123. ^ childView
  124. !
  125. childView: aView
  126. childView := aView.
  127. self update
  128. ! !
  129. !MKContainerView methodsFor: 'rendering'!
  130. renderContentOn: html
  131. html with: self childView
  132. ! !
  133. !MKContainerView class methodsFor: 'instance creation'!
  134. childView: aView
  135. ^ self new
  136. childView: aView;
  137. yourself
  138. ! !
  139. MKSingleAspectView subclass: #MKLabelView
  140. instanceVariableNames: ''
  141. package: 'Moka-Views'!
  142. !MKLabelView commentStamp!
  143. I am an label view. I display a `String`.!
  144. !MKLabelView methodsFor: 'accessing'!
  145. cssClass
  146. ^ super cssClass, ' mk_label'
  147. ! !
  148. !MKLabelView methodsFor: 'defaults'!
  149. defaultControllerClass
  150. ^ super defaultControllerClass
  151. !
  152. defaultLayout
  153. ^ MKLabelLayout new
  154. height: 24;
  155. top: 0;
  156. left:0;
  157. right: 0;
  158. textAlign: 'left';
  159. yourself
  160. ! !
  161. !MKLabelView methodsFor: 'layout'!
  162. textAlign: aString
  163. self layout textAlign: aString
  164. ! !
  165. !MKLabelView methodsFor: 'rendering'!
  166. renderContentOn: html
  167. html with: self aspectValue
  168. ! !
  169. MKLabelView subclass: #MKHeadingView
  170. instanceVariableNames: 'level'
  171. package: 'Moka-Views'!
  172. !MKHeadingView commentStamp!
  173. I display a heading, with a `level` from 1 to 6.!
  174. !MKHeadingView methodsFor: 'accessing'!
  175. cssClass
  176. ^ String streamContents: [ :stream |
  177. stream
  178. << super cssClass
  179. << ' mk_heading level'
  180. << self level asString ]
  181. !
  182. level
  183. ^ level ifNil: [ 1 ]
  184. !
  185. level: aNumber
  186. level := aNumber
  187. !
  188. tag
  189. ^ 'h', self level asString
  190. ! !
  191. MKView subclass: #MKOverlayView
  192. instanceVariableNames: 'childView'
  193. package: 'Moka-Views'!
  194. !MKOverlayView methodsFor: 'accessing'!
  195. childView
  196. ^ childView
  197. !
  198. childView: aView
  199. childView := aView
  200. !
  201. cssClass
  202. ^ super cssClass, ' mk_overlay'
  203. ! !
  204. !MKOverlayView methodsFor: 'actions'!
  205. remove
  206. super remove.
  207. self childView remove
  208. ! !
  209. !MKOverlayView methodsFor: 'defaults'!
  210. defaultControllerClass
  211. ^ MKOverlayController
  212. !
  213. renderContentOn: html
  214. "Left empty on purpose.
  215. No Content is rendered, as the childView is actually displayed separately"
  216. ! !
  217. !MKOverlayView class methodsFor: 'instance creation'!
  218. childView: aView
  219. ^ self new
  220. childView: aView;
  221. yourself
  222. ! !
  223. MKView subclass: #MKPaneView
  224. instanceVariableNames: 'views'
  225. package: 'Moka-Views'!
  226. !MKPaneView commentStamp!
  227. I am a view containing other views.
  228. ## API
  229. Use `#addView:` to add a view to the pane.!
  230. !MKPaneView methodsFor: 'accessing'!
  231. cssClass
  232. ^ super cssClass, ' mk_pane'
  233. !
  234. views
  235. ^ views ifNil: [ views := OrderedCollection new ]
  236. ! !
  237. !MKPaneView methodsFor: 'adding'!
  238. addView: aView
  239. self views add: aView
  240. ! !
  241. !MKPaneView methodsFor: 'defaults'!
  242. defaultLayout
  243. ^ MKPaneLayout new
  244. left: 0;
  245. top: 0;
  246. right: 0;
  247. bottom: 0;
  248. yourself
  249. ! !
  250. !MKPaneView methodsFor: 'layout'!
  251. borderBottom: aNumber
  252. self layout borderBottom: aNumber
  253. !
  254. borderLeft: aNumber
  255. self layout borderLeft: aNumber
  256. !
  257. borderRight: aNumber
  258. self layout borderRight: aNumber
  259. !
  260. borderTop: aNumber
  261. self layout borderTop: aNumber
  262. ! !
  263. !MKPaneView methodsFor: 'rendering'!
  264. renderContentOn: html
  265. self views do: [ :each |
  266. html with: each ]
  267. ! !
  268. MKPaneView subclass: #MKModalPaneView
  269. instanceVariableNames: 'overlay closeOnEnter closeOnClick'
  270. package: 'Moka-Views'!
  271. !MKModalPaneView methodsFor: 'accessing'!
  272. closeOnClick
  273. ^ closeOnClick ifNil: [ false ]
  274. !
  275. closeOnClick: aBoolean
  276. closeOnClick := aBoolean
  277. !
  278. closeOnEnter
  279. ^ closeOnEnter ifNil: [ false ]
  280. !
  281. closeOnEnter: aBoolean
  282. closeOnEnter := aBoolean
  283. !
  284. cssClass
  285. ^ super cssClass, ' mk_modal'
  286. !
  287. overlay
  288. ^ overlay ifNil: [ overlay := MKOverlayView childView: self ]
  289. !
  290. zindex
  291. ^ 1001
  292. ! !
  293. !MKModalPaneView methodsFor: 'defaults'!
  294. defaultControllerClass
  295. ^ MKModalPaneController
  296. !
  297. defaultLayout
  298. ^ super defaultLayout
  299. centerY: 0;
  300. centerX: 0;
  301. width: 300;
  302. height: 200;
  303. yourself
  304. ! !
  305. !MKModalPaneView methodsFor: 'rendering'!
  306. renderOn: html
  307. super renderOn: html.
  308. root at: 'tabindex' put: '0'.
  309. root asJQuery focus.
  310. html with: self overlay
  311. ! !
  312. MKPaneView subclass: #MKPanelView
  313. instanceVariableNames: ''
  314. package: 'Moka-Views'!
  315. !MKPanelView commentStamp!
  316. I am similar to a `MKPaneView` but I am scrollable and display a light background.!
  317. !MKPanelView methodsFor: 'accessing'!
  318. cssClass
  319. ^ super cssClass, ' mk_panel'
  320. ! !
  321. MKAspectsView subclass: #MKSelectionView
  322. instanceVariableNames: 'selectionAspect collectionAspect'
  323. package: 'Moka-Views'!
  324. !MKSelectionView commentStamp!
  325. I an abstract selection view of a list of elements.!
  326. !MKSelectionView methodsFor: 'accessing'!
  327. collection
  328. ^ self valueForAspect: self collectionAspect
  329. !
  330. collectionAspect
  331. ^ collectionAspect
  332. !
  333. collectionAspect: aSelector
  334. collectionAspect := aSelector
  335. !
  336. selectedItem
  337. ^ self valueForAspect: self selectionAspect
  338. !
  339. selectionAspect
  340. ^ selectionAspect
  341. !
  342. selectionAspect: aSelector
  343. selectionAspect := aSelector
  344. ! !
  345. !MKSelectionView methodsFor: 'defaults'!
  346. defaultDisplayBlock
  347. ^ [ :item | item asString ]
  348. ! !
  349. !MKSelectionView class methodsFor: 'instance creation'!
  350. model: aModel collectionAspect: collectionSelector selectionAspect: selectionSelector
  351. ^ (self model: aModel)
  352. collectionAspect: collectionSelector;
  353. selectionAspect: selectionSelector;
  354. yourself
  355. ! !
  356. MKSelectionView subclass: #MKDropdownView
  357. instanceVariableNames: 'modalPaneView listView'
  358. package: 'Moka-Views'!
  359. !MKDropdownView commentStamp!
  360. I am a push button view. My default controller is `MKButtonController`.
  361. My controller must answer to `#onPressed`.
  362. ## API
  363. - Instances can be set a `default` button
  364. - Use `#label:` to set the label string!
  365. !MKDropdownView methodsFor: 'accessing'!
  366. cssClass
  367. ^ super cssClass, ' mk_dropdown'
  368. !
  369. selectedListItem
  370. ^ (root asJQuery find: ':selected') text
  371. !
  372. tag
  373. ^ 'button'
  374. !
  375. update: anAnnouncement
  376. ({self selectionAspect. self collectionAspect}
  377. includes: anAnnouncement aspect) ifTrue: [
  378. self update ]
  379. ! !
  380. !MKDropdownView methodsFor: 'actions'!
  381. popupList
  382. "Show a new list view inside a modal pane"
  383. self modalPaneView render.
  384. self listView focus
  385. ! !
  386. !MKDropdownView methodsFor: 'defaults'!
  387. defaultControllerClass
  388. ^ MKDropdownController
  389. !
  390. defaultLayout
  391. ^ super defaultLayout
  392. width: 120;
  393. height: 24;
  394. yourself
  395. ! !
  396. !MKDropdownView methodsFor: 'rendering'!
  397. renderContentOn: html
  398. html div class: 'mk_dropdown_arrows'.
  399. html with: self selectedItem
  400. ! !
  401. !MKDropdownView methodsFor: 'views'!
  402. listView
  403. ^ listView ifNil: [
  404. listView := (MKDropdownListView
  405. model: self model
  406. collectionAspect: self collectionAspect
  407. selectionAspect: self selectionAspect)
  408. width: self width;
  409. height: 'auto';
  410. yourself ]
  411. !
  412. modalPaneView
  413. ^ modalPaneView ifNil: [
  414. modalPaneView := MKModalPaneView new
  415. extraCssClass: 'mk_dropdown_pane';
  416. closeOnEnter: true;
  417. closeOnClick: true;
  418. addView: self listView;
  419. left: self domPosition x;
  420. top: self domPosition y;
  421. "Max height of the list"
  422. height: 400;
  423. yourself ]
  424. ! !
  425. MKSelectionView subclass: #MKListView
  426. instanceVariableNames: 'displayBlock'
  427. package: 'Moka-Views'!
  428. !MKListView commentStamp!
  429. I display a list of elements in a list control field.!
  430. !MKListView methodsFor: 'accessing'!
  431. activeItem
  432. ^ self findItemFor: (root asJQuery find: '.', self selectedCssClass)
  433. !
  434. cssClass
  435. ^ super cssClass, ' mk_list'
  436. !
  437. displayBlock
  438. ^ displayBlock ifNil: [ self defaultDisplayBlock ]
  439. !
  440. displayBlock: aBlock
  441. displayBlock := aBlock
  442. !
  443. findItemFor: aListItem
  444. ^ aListItem asJQuery data at: 'item'
  445. !
  446. findListItemFor: anObject
  447. ^ (((root asJQuery find: 'li')
  448. filter: [ :thisArg | (thisArg asJQuery data: 'item') = anObject ] currySelf) eq: 0)
  449. !
  450. selectedCssClass
  451. ^ 'selected'
  452. !
  453. tag
  454. ^ 'ul'
  455. ! !
  456. !MKListView methodsFor: 'actions'!
  457. activateItem: anObject
  458. self activateListItem: (self findListItemFor: anObject)
  459. !
  460. activateListItem: aListItem
  461. | item |
  462. (aListItem get: 0) ifNil: [ ^ self ].
  463. aListItem parent children removeClass: self selectedCssClass.
  464. aListItem addClass: self selectedCssClass.
  465. self ensureVisible: aListItem
  466. ! !
  467. !MKListView methodsFor: 'defaults'!
  468. defaultControllerClass
  469. ^ MKListController
  470. !
  471. defaultDisplayBlock
  472. ^ [ :item | item asString ]
  473. ! !
  474. !MKListView methodsFor: 'private'!
  475. ensureVisible: aListItem
  476. "Move the scrollbar to show the active element"
  477. | parent position |
  478. (aListItem get: 0) ifNil: [ ^ self ].
  479. position := self positionOf: aListItem.
  480. parent := aListItem parent.
  481. aListItem position top < 0 ifTrue: [
  482. (parent get: 0) scrollTop: ((parent get: 0) scrollTop + aListItem position top - 10) ].
  483. aListItem position top + aListItem height > parent height ifTrue: [
  484. (parent get: 0) scrollTop: ((parent get: 0) scrollTop + aListItem height - (parent height - aListItem position top)) +10 ]
  485. !
  486. positionOf: aListItem
  487. "TODO: rewrite in smalltalk"
  488. <return aListItem.parent().children().get().indexOf(aListItem.get(0)) + 1>
  489. ! !
  490. !MKListView methodsFor: 'rendering'!
  491. renderContentOn: html
  492. self collection do: [ :each |
  493. self renderItem: each on: html ].
  494. "make the list focusable"
  495. root at: 'tabindex' put: '0'
  496. !
  497. renderItem: anObject on: html
  498. | li |
  499. li := html li.
  500. li asJQuery data: 'item' put: anObject.
  501. self selectedItem = anObject ifTrue: [
  502. li class: self selectedCssClass ].
  503. li with: (self displayBlock value: anObject)
  504. ! !
  505. !MKListView methodsFor: 'updating'!
  506. update: anAnnouncement
  507. anAnnouncement aspect = self selectionAspect ifTrue: [
  508. self updateSelectedItem ].
  509. anAnnouncement aspect = self collectionAspect ifTrue: [
  510. self update ]
  511. !
  512. updateSelectedItem
  513. self activateItem: self selectedItem
  514. ! !
  515. !MKListView class methodsFor: 'instance creation'!
  516. model: aModel collectionAspect: collectionSelector selectionAspect: selectionSelector
  517. ^ (self model: aModel)
  518. collectionAspect: collectionSelector;
  519. selectionAspect: selectionSelector;
  520. yourself
  521. ! !
  522. MKListView subclass: #MKDropdownListView
  523. instanceVariableNames: ''
  524. package: 'Moka-Views'!
  525. !MKDropdownListView commentStamp!
  526. I am similar to a `MKListView`, but inside a `MKDropdownView`.!
  527. !MKDropdownListView methodsFor: 'accessing'!
  528. cssClass
  529. ^ super cssClass, ' mk_dropdown_list'
  530. !
  531. defaultControllerClass
  532. ^ MKDropdownListController
  533. ! !
  534. MKListView subclass: #MKSourceListView
  535. instanceVariableNames: ''
  536. package: 'Moka-Views'!
  537. !MKSourceListView commentStamp!
  538. I am similar to a `MKListView`, but displayed slightly differently, in a similar way as in the left-side the of Finder in OSX.!
  539. !MKSourceListView methodsFor: 'accessing'!
  540. cssClass
  541. ^ super cssClass, ' mk_sourcelist'
  542. ! !
  543. MKSingleAspectView subclass: #MKTextAreaView
  544. instanceVariableNames: ''
  545. package: 'Moka-Views'!
  546. !MKTextAreaView commentStamp!
  547. I am an text area view. My default controller is `MKAnyKeyInputController`.
  548. My controller must answer to `#onKeyPressed:`.!
  549. !MKTextAreaView methodsFor: 'accessing'!
  550. cssClass
  551. ^ super cssClass, ' mk_textarea'
  552. !
  553. tag
  554. ^ 'textarea'
  555. !
  556. value
  557. ^ root asJQuery val
  558. ! !
  559. !MKTextAreaView methodsFor: 'defaults'!
  560. defaultControllerClass
  561. ^ MKAnyKeyInputController
  562. !
  563. defaultLayout
  564. ^ super defaultLayout
  565. width: 160;
  566. height: 80;
  567. yourself
  568. ! !
  569. !MKTextAreaView methodsFor: 'rendering'!
  570. renderContentOn: html
  571. root with: self aspectValue
  572. ! !
  573. !MKTextAreaView methodsFor: 'updating'!
  574. update
  575. root ifNotNil: [ root asJQuery val: self aspectValue ]
  576. ! !
  577. MKTextAreaView subclass: #MKInputView
  578. instanceVariableNames: ''
  579. package: 'Moka-Views'!
  580. !MKInputView commentStamp!
  581. I am an input view. My default controller is `MKEnterInputController`.
  582. My controller must answer to `#onKeyPressed:`.!
  583. !MKInputView methodsFor: 'accessing'!
  584. cssClass
  585. ^ 'moka_view mk_input'
  586. !
  587. tag
  588. ^ 'input'
  589. ! !
  590. !MKInputView methodsFor: 'defaults'!
  591. defaultControllerClass
  592. ^ MKEnterInputController
  593. !
  594. defaultLayout
  595. ^ super defaultLayout
  596. width: 160;
  597. height: 24;
  598. yourself
  599. ! !
  600. !MKInputView methodsFor: 'rendering'!
  601. renderContentOn: html
  602. root value: self aspectValue
  603. ! !
  604. !MKInputView methodsFor: 'settings'!
  605. triggerChangeOnAnyKey
  606. self controller: MKAnyKeyInputController new
  607. !
  608. triggerChangeOnEnter
  609. self controller: MKEnterInputController new
  610. ! !