您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
1年前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469
  1. <!--
  2. # @name: base.html
  3. # @version: 0.1
  4. # @creation_date: 2021-10-20
  5. # @license: The MIT License <https://opensource.org/licenses/MIT>
  6. # @author: Simon Bowie <ad7588@coventry.ac.uk>
  7. # @purpose: Basic layout for all pages
  8. # @acknowledgements:
  9. # https://www.digitalocean.com/community/tutorials/how-to-make-a-web-application-using-flask-in-python-3
  10. # Bootstrap 5.1.3: https://getbootstrap.com/
  11. # Flask-Moment: https://flask-moment.readthedocs.io/en/latest/
  12. # Boostrap select: https://stackoverflow.com/questions/67942546/bootstrap-5-select-dropdown-with-the-multiple-attribute-collapsed
  13. -->
  14. {% macro view_switch() %}
  15. <div class="flex">
  16. <a
  17. href="{{ url_for(request.endpoint) }}?view=regular{% for key in request.args %}{% if key != 'view' %}&{{ key }}={{ request.args.get(key) }}{% endif %}{% endfor %}"
  18. class="block link flex p-3 gap-2 {% if view != 'list' %} active {% endif %}"
  19. >
  20. <div class="w-5 h-5 inline-block">
  21. <svg width="100%" height="100%" viewBox="0 0 46 36" fill="none" xmlns="http://www.w3.org/2000/svg">
  22. <rect width="21" height="36" fill="black" />
  23. <rect x="27" width="19" height="14" fill="black" />
  24. <rect x="27" y="21" width="19" height="15" fill="black" />
  25. </svg>
  26. </div>
  27. Expanded
  28. </a>
  29. <a
  30. href="{{ url_for(request.endpoint) }}?view=list{% for key in request.args %}{% if key != 'view' %}&{{ key }}={{ request.args.get(key) }}{% endif %}{% endfor %}"
  31. class="block link flex p-3 gap-2 {% if view == 'list' %} active {% endif %}"
  32. >
  33. <div class="w-5 h-5 inline-block ">
  34. <svg width="100%" height="100%" viewBox="0 0 38 36" fill="none" xmlns="http://www.w3.org/2000/svg">
  35. <path d="M0 2H38" stroke="black" stroke-width="2.5" />
  36. <path d="M0 13H38" stroke="black" stroke-width="2.5" />
  37. <path d="M0 24H38" stroke="black" stroke-width="2.5" />
  38. <path d="M0 35H38" stroke="black" stroke-width="2.5" />
  39. </svg>
  40. </div>
  41. List
  42. </a>
  43. </div>
  44. {% endmacro %}
  45. {% macro menu(type) %}
  46. <a
  47. class="block menuitem w-48 "
  48. href="{{ url_for('main.index') }}{% if view == 'list' %}?view=list{% endif %}"
  49. @click="menuOpen = false"
  50. ><span>Experimental Publishing Compendium <span class="ml-3 inline-block -rotate-12 italic text-[#f52d2d]" style="text-shadow: 0 0 0.4rem white">beta</span></span>
  51. </a>
  52. <a href="{{ url_for('tool.get_tools') }}{% if view == 'list' %}?view=list{% endif %}"
  53. class="{{ 'active' if request.path == url_for('tool.get_tools') }} menuitem tool medium-title {% if type=='top' %} hidden lg:block {% else %} block {% endif %}"
  54. @click="menuOpen = false"
  55. >
  56. Tools
  57. </a>
  58. <a href="{{ url_for('practice.get_practices') }}{% if view == 'list' %}?view=list{% endif %}"
  59. class="{{ 'active' if request.path == url_for('practice.get_practices') }} menuitem practice medium-title {% if type=='top' %} hidden lg:block {% else %} block {% endif %}"
  60. @click="menuOpen = false"
  61. >
  62. Practices
  63. </a>
  64. <a href="{{ url_for('book.get_books') }}{% if view == 'list' %}?view=list{% endif %}"
  65. class="{{ 'active' if request.path == url_for('book.get_books') }} menuitem medium-title book block {% if type=='top' %} hidden lg:block {% else %} block {% endif %} "
  66. @click="menuOpen = false"
  67. >
  68. Books
  69. </a>
  70. <a href="{{ url_for('main.about') }}{% if view == 'list' %}?view=list{% endif %}"
  71. class="{{ 'active' if request.path == url_for('main.about') }} menuitem mr-auto {% if type=='top' %} hidden lg:block {% else %} block {% endif %}"
  72. @click="menuOpen = false"
  73. >
  74. About / <br> Contact
  75. </a>
  76. <!-- <div class="ml-auto mr-2">
  77. {% if current_user.is_authenticated %}
  78. <a href="{{ url_for('create.create_resource') }}" class="block link">
  79. Add resource
  80. </a>
  81. {% endif %}
  82. {% if current_user.is_authenticated %}
  83. <a href="{{ url_for('main.profile') }}" class="block link">
  84. Profile
  85. </a>
  86. {% endif %}
  87. {% if not current_user.is_authenticated %}
  88. <a href="{{ url_for('auth.login') }}" class="block link">
  89. Login
  90. </a>
  91. <a href="{{ url_for('auth.signup') }}" class="block link">
  92. Sign Up
  93. </a>
  94. {% endif %}
  95. {% if current_user.is_authenticated %}
  96. <a href="{{ url_for('auth.logout') }}" class="block link">
  97. Logout
  98. </a>
  99. {% endif %}
  100. </div> -->
  101. <form action="{{ url_for('search.basic_search') }}" class="hidden lg:block">
  102. <input type="text" name="query" class="text-center h-full text-base border-l-2 border-black pl-1"
  103. placeholder="Search">
  104. <input class="hidden" type="submit" hidden />
  105. </form>
  106. {% endmacro %}
  107. {% macro relationships_links(resource) %}
  108. {% if resource.tools %}
  109. <div class="">
  110. <div class="px-4 mt-16 lg:mt-0">
  111. Tools
  112. </div>
  113. {% for tool in resource.tools %}
  114. <div class="cell">
  115. {{ resource_lead(tool,size=2) }}
  116. </div>
  117. {% endfor %}
  118. </div>
  119. {% endif %}
  120. {% if resource.books %}
  121. <div class="">
  122. <div class="px-4 mt-16 lg:mt-0">
  123. Books
  124. </div>
  125. {% for book in resource.books %}
  126. <div class="cell">
  127. {{ resource_lead(book,size=2) }}
  128. </div>
  129. {% endfor %}
  130. </div>
  131. {% endif %}
  132. {% if resource.practices %}
  133. <div class="">
  134. <div class="px-4 mt-16 lg:mt-0">
  135. Practices
  136. </div>
  137. {% for practice in resource.practices %}
  138. <div class="cell">
  139. {{ resource_lead(practice,size=2) }}
  140. </div>
  141. {% endfor %}
  142. </div>
  143. {% endif %}
  144. {% endmacro %}
  145. {% macro resource_list(resource, loop, show_number=true) %}
  146. <div class="border-b-2 border-black">
  147. <a class="flex gap-4 h-full py-1 hover:opacity-60 transition-opacity cursor-pointer size-{{ size }}" {% if
  148. resource['type']=='book' %} href="{{ url_for('book.show_book', book_id=resource['id']) }}" {% endif %} {% if
  149. resource['type']=='tool' %} href="{{ url_for('tool.show_tool', tool_id=resource['id']) }}" {% endif %} {% if
  150. resource['type']=='practice' %} href="{{ url_for('practice.show_practice', practice_id=resource['id']) }}" {% endif
  151. %} hx-target="#modal-content" hx-select="main" hx-swap="innerHTML" @click="openModal()">
  152. <div class="w-[4rem] shrink-0 lg:w-[12rem] text-center ">
  153. {% if show_number %}
  154. {{loop.index}} / {{count}}
  155. {% else %}
  156. <div class="capitalize inline-block min-w-[5rem] px-4 ">{{ resource['type'] }}</div>
  157. {% endif %}
  158. </div>
  159. <div>
  160. <span class="{{ resource.type }}">
  161. {{ resource.name }}
  162. </span>
  163. {% if resource['year'] %}
  164. {{ resource['year'] }}{% if resource['author'] %}, {{ resource['author'] }}{% endif%}
  165. {% endif %}
  166. </div>
  167. </a>
  168. </div>
  169. {% endmacro %}
  170. {% macro resource_with_related(resource, loop, show_number=true) %}
  171. <div class="w-full border-b-2 border-black fade-right">
  172. <div class="content w-full py-8 overflow-x-auto">
  173. <div class="grid lg:grid-rows-[auto,auto,auto] grid-flow-col w-fit pr-40 " >
  174. <div class="w-[4rem] shrink-0 lg:w-[12rem] text-center px-2 py-4 ">
  175. {% if show_number %}
  176. <p>{{loop.index}} / {{count}}</p>
  177. {% else %}
  178. <p class="capitalize">{{ resource['type'] }}</p>
  179. {% endif %}
  180. </div>
  181. <div class="cell w-[20rem] lg:w-[32rem] row-start-1 lg:row-span-3">
  182. {{ resource_lead(resource) }}
  183. </div>
  184. {% if resource.tools %}
  185. <div class="grid lg:grid-rows-1 grid-flow-col w-fit h-fit">
  186. <div class="related">
  187. Related<br>Tools
  188. </div>
  189. {% for tool in resource.tools %}
  190. <div class="cell w-[20rem] lg:w-[30rem]">
  191. {{ resource_lead(tool,size=2) }}
  192. </div>
  193. {% endfor %}
  194. </div>
  195. {% endif %}
  196. {% if resource.books %}
  197. <div class="grid grid-rows-1 grid-flow-col w-fit h-fit">
  198. <div class="related">
  199. Related<br>Books
  200. </div>
  201. {% for book in resource.books %}
  202. <div class="cell w-[20] lg:w-[35rem]">
  203. {{ resource_lead(book,size=2) }}
  204. </div>
  205. {% endfor %}
  206. </div>
  207. {% endif %}
  208. {% if resource.practices %}
  209. <div class="grid grid-rows-1 grid-flow-col w-fit h-fit">
  210. <div class="related">
  211. Related<br>Practices
  212. </div>
  213. {% for practice in resource.practices %}
  214. <div class="cell w-[20rem] lg:w-[25rem]">
  215. {{ resource_lead(practice,size=2) }}
  216. </div>
  217. {% endfor %}
  218. </div>
  219. {% endif %}
  220. </div>
  221. </div>
  222. </div>
  223. {% endmacro %}
  224. {% macro popup_link(title,url) %}
  225. <a href="{{ url }}" class="nav-link">
  226. {{ title }}
  227. </a>
  228. {% endmacro %}
  229. {% macro resource_lead(resource,size=1) %}
  230. <a
  231. class="block h-full hover:opacity-60 transition-opacity cursor-pointer size-{{ size }}"
  232. {% if resource['type'] == 'book' %}
  233. href="{{ url_for('book.show_book', book_id=resource['id']) }}"
  234. {% endif %}
  235. {% if resource['type'] == 'tool' %}
  236. href="{{ url_for('tool.show_tool', tool_id=resource['id']) }}"
  237. {% endif %}
  238. {% if resource['type'] == 'practice' %}
  239. href="{{ url_for('practice.show_practice', practice_id=resource['id']) }}"
  240. {% endif %}
  241. hx-target="#modal-content"
  242. hx-select="main"
  243. hx-swap="innerHTML"
  244. @click="openModal()"
  245. >
  246. {% if (resource.type == 'book') and (resource.references) %}
  247. <img class="w-20 h-20 object-contain float-right m-4 grayscale rotate-[15deg]" src="{{resource.references}}" alt="cover for {{ resource.name }}">
  248. {% endif %}
  249. <h2 class="{{ resource['type'] }} {% if size==1 %} big-title {% else %} small-title {% endif %} mb-2">{{ resource['name'] }}</h2>
  250. {% if resource['year'] %}
  251. <div class="text-sm">
  252. {{ resource['year'] }}{% if resource['author'] %}, {{ resource['author'] }}{% endif%}
  253. </div>
  254. {% endif %}
  255. <div class="{% if size==1 %} big-text {% else %} small-text {% endif %} mb-[1em]">
  256. {{ resource['description'] | truncate(150) }}
  257. </div>
  258. </a>
  259. {% if current_user.is_authenticated %}
  260. <div class="">
  261. {% if resource['type'] == 'tool' %}
  262. <a href="{{ url_for('tool.edit_tool', tool_id=resource['id']) }}">
  263. <span class="absolute top-0 left-0 text-xs">Edit</span>
  264. </a>
  265. {% elif resource['type'] == 'practice' %}
  266. <a href="{{ url_for('practice.edit_practice', practice_id=resource['id']) }}">
  267. <span class="absolute top-0 left-0 text-xs">Edit</span>
  268. </a>
  269. {% elif resource['type'] == 'book' %}
  270. <a href="{{ url_for('book.edit_book', book_id=resource['id']) }}">
  271. <span class="absolute top-0 left-0 text-xs">Edit</span>
  272. </a>
  273. {% endif %}
  274. </div>
  275. {% endif %}
  276. {% endmacro %}
  277. <!DOCTYPE html>
  278. <html lang="en">
  279. <head>
  280. {{ moment.include_moment() }}
  281. <meta charset="utf-8">
  282. <meta http-equiv="X-UA-Compatible" content="IE=edge">
  283. <meta name="viewport" content="width=device-width, initial-scale=1">
  284. <title>Experimental Publishing Compendium</title>
  285. <script src="{{ url_for('static',filename='js/alpine.min.js') }}" defer></script>
  286. <script src="{{ url_for('static',filename='js/htmx.min.js') }}"></script>
  287. <link href="{{ url_for('static',filename='styles/main.css') }}" rel="stylesheet">
  288. <script>
  289. // htmx.on('htmx:beforeRequest', e=> {
  290. // console.log(e)
  291. // })
  292. // htmx.logAll()
  293. function base() {
  294. return {
  295. menuOpen: false,
  296. modalOpen: false,
  297. showRelated: false,
  298. home: '/',
  299. hideIfBase() {
  300. let str = document.location.toString();
  301. str = str.replace('http://', '');
  302. str = str.replace('https://', '');
  303. let l = str.split('/').length - 1;
  304. if (l < 2) {
  305. this.modalOpen = false;
  306. }
  307. return l;
  308. },
  309. init() {
  310. this.$watch('document.location', (value, oldValue) => {
  311. console.log('new url', value);
  312. });
  313. window.addEventListener('popstate', e => {
  314. this.hideIfBase();
  315. });
  316. },
  317. hideOverlay() {
  318. this.modalOpen = false;
  319. // window.history.pushState({}, '', this.home);
  320. },
  321. openModal() {
  322. // this.$refs.modal.innerHTML = '';
  323. this.modalOpen = true;
  324. let mc = document.querySelector('#modal-content');
  325. mc.scrollTo(0, 0);
  326. }
  327. }
  328. }
  329. </script>
  330. </head>
  331. <body
  332. class="text-base overflow-y-scroll"
  333. x-data="base()"
  334. hx-boost="true"
  335. hx-select="#all"
  336. hx-target="#all"
  337. hx-swap="outerHTML"
  338. hx-indicator="body"
  339. >
  340. <div id="loading" class="loading pointer-events-none ">
  341. <!-- <div class="bg-white fixed top-0 left-0 w-full h-screen z-20"></div> -->
  342. <div class="bg fixed top-0 left-0 w-full h-screen z-30"></div>
  343. <div class="spinner fixed top-0 left-0 w-full h-screen z-[60] flex justify-center items-center">
  344. <div class="inner bg-black rounded-full px-8 py-6 text-white text-center">
  345. Loading...
  346. </div>
  347. </div>
  348. </div>
  349. <div id="all">
  350. <header class="sticky top-0 z-10 bg-white border-b-[2px] border-black flex justify-between w-full">
  351. <nav class=" lg:flex justify-center items-stretch w-full ">
  352. {{ menu('top') }}
  353. </nav>
  354. <div class="burger h-14 cursor-pointer block lg:hidden p-4" @click="menuOpen = true">
  355. <svg width="100%" height="100%" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
  356. <path d="M1 4H19" stroke="black" stroke-width="1.5" />
  357. <path d="M19 9H1" stroke="black" stroke-width="1.5" />
  358. <path d="M1 14H19" stroke="black" stroke-width="1.5" />
  359. </svg>
  360. </div>
  361. </header>
  362. <menu
  363. class="fixed top-0 right-0 z-20 p-8 bg-white w-[calc(100%-2rem)] max-w-[30rem] h-screen"
  364. x-show="menuOpen"
  365. style="box-shadow: 0 0 2rem rgb(0,0,0,0.2);"
  366. @click.outside = "menuOpen = false"
  367. >
  368. {{ menu('side') }}
  369. </menu>
  370. <!-- Begin page content -->
  371. <main>
  372. {% with messages = get_flashed_messages() %}
  373. {% if messages %}
  374. <div class="alert alert-danger">
  375. {{ messages[0] }}
  376. </div>
  377. {% endif %}
  378. {% endwith %}
  379. {% block content %}
  380. {% endblock %}
  381. </main>
  382. </div>
  383. <div id="modal" x-show="modalOpen" class="modal h-screen w-full fixed top-0 z-50 p-4 bg overscroll-none">
  384. <div class="cross cursor-pointer absolute top-10 right-10 w-10 h-10" @click="hideOverlay()">
  385. <svg width="100%" height="100%" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
  386. <path d="M2 2L18 18M18 2L2 18" stroke="black" stroke-width="1"></path>
  387. </svg>
  388. </div>
  389. <div id="modal-content" @click.outside="hideOverlay()" x-ref="modal" class="content w-full overflow-y-auto h-full bg-white " >
  390. </div>
  391. </div>
  392. <!-- Sticky footer-->
  393. <footer class="std-margin mt-20 text-sm">
  394. <div class="container">
  395. <span class="">© 2022–{{ moment().format('YYYY') }} <a href="https://copim.ac.uk/">COPIM</a> and licensed under a <a href="https://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License (CC BY 4.0)</a>.</span>
  396. </div>
  397. </footer>
  398. <!-- JavaScript -->
  399. <!-- jQuery first, then Popper JS, then Bootstrap JS -->
  400. <script src="{{ url_for('static',filename='js/main.js') }}"></script>
  401. </body>
  402. </html>