DropDownWithActions.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <template>
  2. <div>
  3. <div>
  4. <button
  5. v-on:click="toggle()"
  6. v-on:blur="debounce()"
  7. class="btn"
  8. :class="btnClass"
  9. >
  10. {{ btnLabel }}
  11. <span :class="btnStatus"></span>
  12. </button>
  13. <div v-on:focus="hold()">
  14. <ul v-show="showing" class="ddown" v-on:focus="hold()">
  15. <li
  16. v-for="current in actions"
  17. v-on:focus="hold()"
  18. v-bind:key="current.id"
  19. >
  20. <a
  21. href="#"
  22. :data-value="current.value"
  23. :data-result="current.result"
  24. v-on:focus="hold()"
  25. v-on:blur="debounce()"
  26. v-on:click.stop.prevent="doAction($event)"
  27. v-on:keydown.space="doAction($event)"
  28. >
  29. {{ current.name }}
  30. </a>
  31. </li>
  32. </ul>
  33. </div>
  34. </div>
  35. </div>
  36. </template>
  37. <script>
  38. export default {
  39. name: "DropDownWithActions",
  40. props: ["actions", "condition", "endpoint", "id"],
  41. mounted() {
  42. this.initData();
  43. },
  44. data() {
  45. return {
  46. blob: "",
  47. errorPrompt: "",
  48. holding: false,
  49. pristine: true,
  50. showing: false,
  51. status: "",
  52. timer: "",
  53. updating: false
  54. };
  55. },
  56. computed: {
  57. btnClass: function() {
  58. //TODO: If we want general reuse, this needs currying and
  59. // to get logic as a function from the props
  60. return this.updating
  61. ? "btn-default"
  62. : this.status === "Scheduled"
  63. ? "btn-primary"
  64. : this.status === "Checked In"
  65. ? "btn-success"
  66. : this.status === "Active"
  67. ? "btn-info"
  68. : this.status === "Canceled" ||
  69. this.status === "Finished" ||
  70. this.status === "No-show"
  71. ? "btn-warning"
  72. : "btn-danger";
  73. },
  74. btnLabel: function() {
  75. return this.updating ? "Loading..." : this.status;
  76. },
  77. btnStatus: function() {
  78. return this.updating
  79. ? "glyphicon glyphicon-transfer"
  80. : this.errorPrompt
  81. ? "glyphicon glyphicon-alert"
  82. : this.status !== this.condition || !this.pristine
  83. ? "glyphicon glyphicon-saved"
  84. : "caret";
  85. }
  86. },
  87. methods: {
  88. hold: function() {
  89. clearTimeout(this.timer);
  90. this.holding = true;
  91. },
  92. debounce: function() {
  93. this.holding = false;
  94. clearTimeout(this.timer);
  95. this.timer = setTimeout(
  96. function() {
  97. this.showing = this.holding;
  98. }.bind(this),
  99. 100
  100. );
  101. },
  102. toggle: function() {
  103. this.showing = !this.showing;
  104. },
  105. doAction: function(event) {
  106. this.pristine = false;
  107. this.updating = true;
  108. this.showing = false;
  109. this.holding = false;
  110. const payload = { id: this.id, condition: event.target.dataset.value };
  111. // console.log(this.endpoint)
  112. // console.info(JSON.stringify(payload))
  113. if (this.endpoint) {
  114. fetch(this.endpoint, {
  115. method: "POST",
  116. cache: "no-cache",
  117. headers: {
  118. "Content-Type": "application/json"
  119. },
  120. body: JSON.stringify(payload)
  121. })
  122. .then(response => response.json())
  123. .then(json => {
  124. if (json && json.status === "success") {
  125. this.blob = json;
  126. this.status = event.target.dataset.result;
  127. this.$emit("update", this.status);
  128. this.updating = false;
  129. } else {
  130. this.errorPrompt = "Response failed.";
  131. this.updating = false;
  132. }
  133. })
  134. .catch(error => {
  135. this.errorPrompt = `Request failed. ${error}`;
  136. this.updating = false;
  137. });
  138. } else {
  139. this.errorPrompt = "No configuration.";
  140. this.updating = false;
  141. }
  142. },
  143. initData: function() {
  144. this.blob = "";
  145. this.errorPrompt = "";
  146. this.holding = false;
  147. this.showing = false;
  148. this.status = this.condition;
  149. this.timer = "";
  150. this.updating = false;
  151. this.pristine = true;
  152. }
  153. },
  154. watch: {
  155. condition() {
  156. this.initData();
  157. },
  158. id() {
  159. this.initData();
  160. },
  161. blob() {
  162. console.info("blob");
  163. console.info(JSON.stringify(this.blob));
  164. },
  165. errorPrompt() {
  166. console.info("errs");
  167. console.error(JSON.stringify(this.errorPrompt));
  168. }
  169. }
  170. };
  171. </script>
  172. <style scoped>
  173. ul.ddown {
  174. position: absolute;
  175. padding: 0;
  176. margin: 0;
  177. list-style-type: none;
  178. box-shadow: 0 0 1px rgba(0, 0, 0, 0.42);
  179. background: red;
  180. z-index: 10;
  181. }
  182. ul.ddown li {
  183. padding: 0;
  184. background: white;
  185. text-decoration: none;
  186. }
  187. ul.ddown li a {
  188. display: block;
  189. padding: 3px 20px;
  190. clear: both;
  191. font-weight: 400;
  192. line-height: 1.44;
  193. white-space: nowrap;
  194. }
  195. ul.ddown li a:hover,
  196. ul.ddown li a:focus {
  197. background-color: #f3f4f5;
  198. }
  199. </style>