project-open-window.vue 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. <template>
  2. <div class="project-open-window">
  3. <project-creation-window v-if="create"
  4. :status="status"
  5. :socket="socket"
  6. @cancel="create = false"></project-creation-window>
  7. <template v-else>
  8. <div class="title">
  9. <h1>Open Project</h1>
  10. Click a project to open it or create a new one.
  11. </div>
  12. <div class="projects">
  13. <div class="project"
  14. v-for="project in sortedProjects"
  15. :key="project.id"
  16. @click="load(project.index)">
  17. <h2>{{ project.name }}</h2>
  18. <div class="description">{{ project.description }}</div>
  19. <div>
  20. {{ datetime(project.created) }}
  21. </div>
  22. </div>
  23. <div v-if="sortedProjects.length === 0"
  24. class="project">
  25. <h2>There are no projects available.</h2>
  26. <div class="description">Please use the button at the bottom of the page to create a new one.</div>
  27. </div>
  28. </div>
  29. <div class="footer">
  30. <button-input @click="create = true" type="primary">
  31. New Project
  32. </button-input>
  33. </div>
  34. </template>
  35. </div>
  36. </template>
  37. <script>
  38. import ButtonInput from "@/components/base/button-input";
  39. import ProjectCreationWindow from "@/components/projects/project-creation-window";
  40. export default {
  41. name: "project-open-window",
  42. components: {ProjectCreationWindow, ButtonInput},
  43. props: ['projects', 'status', 'socket'],
  44. data: function() {
  45. return {
  46. create: false
  47. }
  48. },
  49. computed: {
  50. sortedProjects: function() {
  51. return [...this.projects].map((e, i) => {
  52. e.index = i;
  53. return e;
  54. }).sort((a, b) => a.name < b.name ? -1 : 1);
  55. }
  56. },
  57. methods: {
  58. load: function(index) {
  59. this.$emit('open', this.projects[index]);
  60. },
  61. datetime: function(timestamp) {
  62. const date = new Date(timestamp * 1000);
  63. const da = date.getDate();
  64. const mo = date.getMonth() + 1;
  65. const ye = date.getFullYear();
  66. const ho = date.getHours();
  67. const mi = date.getMinutes();
  68. return [
  69. [
  70. da < 10 ? '0' + da : da,
  71. mo < 10 ? '0' + mo : mo,
  72. ye
  73. ].join('.'),
  74. [
  75. ho < 10 ? '0' + ho : ho,
  76. mi < 10 ? '0' + mi : mi
  77. ].join(':')
  78. ].join(' ');
  79. }
  80. }
  81. }
  82. </script>
  83. <style scoped>
  84. .project-open-window {
  85. display: flex;
  86. flex-direction: column;
  87. }
  88. h1 {
  89. margin: 0 0 0.4rem 0;
  90. }
  91. h2 {
  92. margin: 0;
  93. font-size: 120%;
  94. font-family: "Roboto Condensed";
  95. }
  96. .title {
  97. margin: 0;
  98. padding: 2rem;
  99. border-bottom: 1px solid rgba(0, 0, 0, 0.2);
  100. flex-grow: 0;
  101. }
  102. .projects {
  103. overflow: auto;
  104. flex-grow: 1;
  105. }
  106. .project:not(:last-child) {
  107. border-bottom: 1px solid rgba(0, 0, 0, 0.2);
  108. }
  109. .project {
  110. padding: 1rem 2rem;
  111. cursor: pointer;
  112. }
  113. h2, .description {
  114. text-overflow: ellipsis;
  115. white-space: nowrap;
  116. overflow: hidden;
  117. }
  118. .footer {
  119. padding: 2rem;
  120. display: flex;
  121. justify-content: flex-end;
  122. }
  123. </style>