diff --git a/README.md b/README.md index d2023f3d5..0b5c68d5e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Eyra Mono -Primary collection of Eyra projects +Primary collection of Eyra projects ## Projects @@ -19,7 +19,7 @@ Project implementing a SaaS platform based on interlinked modules called Systems * Campaign * Assignment * Lab -* Survey +* Questionnaire * Pool * Data Donation * .. @@ -28,12 +28,12 @@ Project implementing a SaaS platform based on interlinked modules called Systems * Next -Primary bundle with all features available except Link specific features. +Primary bundle with all features available except Link specific features. Next is hosted on: https://eyra.co * Link -Secundary bundle with only Panl specific features. +Secundary bundle with only Panl specific features. Link is hosted on: https://researchpanl.eu ## Banking Proxy diff --git a/core/assets/js/app.js b/core/assets/js/app.js index c66003216..751d48c6e 100644 --- a/core/assets/js/app.js +++ b/core/assets/js/app.js @@ -26,6 +26,7 @@ import "./100vh-fix"; import { ViewportResize } from "./viewport_resize"; import { SidePanel } from "./side_panel"; import { Toggle } from "./toggle"; +import { Cell } from "./cell"; import { LiveContent, LiveField } from "./live_content"; import { Tabbar, TabbarItem, TabbarFooterItem } from "./tabbar"; import { PythonUploader } from "./python_uploader"; @@ -96,6 +97,7 @@ let Hooks = { ViewportResize, SidePanel, Toggle, + Cell, LiveContent, LiveField, Tabbar, diff --git a/core/assets/js/cell.js b/core/assets/js/cell.js new file mode 100644 index 000000000..532ae64ac --- /dev/null +++ b/core/assets/js/cell.js @@ -0,0 +1,82 @@ +const COLLAPSED = "collapsed"; +const EXPANDED = "expanded"; + +const COLLAPSED_VIEW = "cell-collapsed-view"; +const EXPANDED_VIEW = "cell-expanded-view"; + +const COLLAPSE_BUTTON = "cell-collapse-button"; +const EXPAND_BUTTON = "cell-expand-button"; + +export const Cell = { + mounted() { + this.collapseButton = this.el.getElementsByClassName(COLLAPSE_BUTTON)[0]; + this.collapsedView = this.el.getElementsByClassName(COLLAPSED_VIEW)[0]; + + this.expandButton = this.el.getElementsByClassName(EXPAND_BUTTON)[0]; + this.expandedView = this.el.getElementsByClassName(EXPANDED_VIEW)[0]; + + this.collapseButton.addEventListener("click", (event) => { + event.stopPropagation(); + this.updateStatus(COLLAPSED); + }); + + this.expandButton.addEventListener("click", (event) => { + event.stopPropagation(); + this.updateStatus(EXPANDED); + }); + + var initialStatus = this.el.dataset.initialStatus + ? this.el.dataset.initialTab + : COLLAPSED; + + var savedStatus = this.loadStatus(); + this.status = savedStatus ? savedStatus : initialStatus; + this.updateUI(); + }, + + updated() { + this.updateUI(); + }, + + loadStatus() { + const key = this.getStatusKey(); + const status = window.localStorage.getItem(key); + if (typeof status === "string") { + return status; + } + return undefined; + }, + + saveStatus() { + console.info("saveStatus ", this.status); + window.localStorage.setItem(this.getStatusKey(), this.status); + }, + + getStatusKey() { + return "cell://" + this.el.id + "/status"; + }, + + updateStatus(status) { + this.status = status; + this.saveStatus(); + this.updateUI(); + }, + + updateUI() { + if (this.status == EXPANDED) { + this.hide(this.collapsedView); + this.show(this.expandedView); + } else { + this.show(this.collapsedView); + this.hide(this.expandedView); + } + }, + hide(element) { + if (!element.classList.contains("hidden")) { + element.classList.add("hidden"); + } + }, + show(element) { + element.classList.remove("hidden"); + }, +}; diff --git a/core/assets/js/side_panel.js b/core/assets/js/side_panel.js index e3f3a3a70..9cb5cac3a 100644 --- a/core/assets/js/side_panel.js +++ b/core/assets/js/side_panel.js @@ -1,54 +1,42 @@ -const maxBottomMargin = 62; +const maxBottomMargin = 63; export const SidePanel = { mounted() { + this.mainContent = document.getElementById("main-content"); this.parent = document.getElementById(this.el.dataset.parent); this.panel = this.el.getElementsByClassName("panel")[0]; - this.panel.style = `height: 0px;`; - this.make_absolute(); + this.panel.style = `position: fixed; height: 0px; top: 0px`; this.updateFrame(); - window.addEventListener("tab-activated", (event) => { + new ResizeObserver(() => { this.updateFrame(); - }); + }).observe(this.parent); - window.addEventListener("scroll", (event) => { + this.mainContent.addEventListener("scroll", (event) => { this.updateFrame(); }); window.addEventListener("resize", (event) => { this.updateFrame(); }); - }, - make_absolute() { - this.el.classList.remove("relative"); - this.el.classList.add("absolute"); + + window.addEventListener("tab-activated", (event) => { + this.updateFrame(); + }); }, updated() { - this.make_absolute(); this.updateFrame(); }, updateFrame() { - this.updateHeight(); - this.updatePosition(); - }, - updateHeight() { - const bottomMarginDelta = Math.min( - maxBottomMargin, - document.documentElement.scrollHeight - - window.scrollY - - window.innerHeight - ); - const bottomMargin = maxBottomMargin - bottomMarginDelta; + const bottomDistance = + this.mainContent.scrollHeight - + this.mainContent.scrollTop - + window.innerHeight; + const bottomMargin = + maxBottomMargin - Math.min(maxBottomMargin, bottomDistance); + const topMargin = Math.max(0, this.parent.getBoundingClientRect().top); + const height = window.innerHeight - (topMargin + bottomMargin); - const height = - window.innerHeight - - (this.parent.getBoundingClientRect().top + bottomMargin); - this.panel.style = `height: ${height}px;`; - }, - updatePosition() { - const top = - Math.max(0, this.parent.getBoundingClientRect().top) + window.scrollY; - this.el.style = `top: ${top}px; right: 0px`; + this.panel.style = `position: fixed; height: ${height}px; top: ${topMargin}px`; }, }; diff --git a/core/assets/js/tabbar.js b/core/assets/js/tabbar.js index 18af8d362..eff8758db 100644 --- a/core/assets/js/tabbar.js +++ b/core/assets/js/tabbar.js @@ -63,7 +63,7 @@ export const Tabbar = { var isVisible = tab.id === nextTabId; setVisible(tab, isVisible); if (isVisible) { - tab.dispatchEvent(new Event("tab-activated", { bubbles: true})); + tab.dispatchEvent(new Event("tab-activated", { bubbles: true })); } }); diff --git a/core/assets/static/images/icons/ready.svg b/core/assets/static/images/icons/ready.svg new file mode 100644 index 000000000..a5f0811bb --- /dev/null +++ b/core/assets/static/images/icons/ready.svg @@ -0,0 +1,11 @@ + diff --git a/core/assets/tailwind.config.js b/core/assets/tailwind.config.js index d7deef92b..8f02ec07a 100644 --- a/core/assets/tailwind.config.js +++ b/core/assets/tailwind.config.js @@ -239,7 +239,7 @@ module.exports = { paddingBottom: "constant(safe-area-inset-bottom)", paddingBottom: "env(safe-area-inset-bottom)", }, - ".scrollbar-hide": { + ".scrollbar-hidden": { /* Firefox */ "scrollbar-width": "thin", diff --git a/core/bundles/link/seeds.exs b/core/bundles/link/seeds.exs index 14e2ce202..fd5e82c21 100644 --- a/core/bundles/link/seeds.exs +++ b/core/bundles/link/seeds.exs @@ -15,11 +15,11 @@ student_count = 1500 researcher_count = 100 researchers_per_campaign = 5 lab_count = 200 -survey_count = 600 +questionnaire_count = 600 time_slots_per_lab = 20 seats_per_time_slot = 20 -survey_url = "https://vuamsterdam.eu.qualtrics.com/jfe/form/SV_4Po8iTxbvcxtuaW" +questionnaire_url = "https://vuamsterdam.eu.qualtrics.com/jfe/form/SV_4Po8iTxbvcxtuaW" images = [ "raw_url=https%3A%2F%2Fimages.unsplash.com%2Fphoto-1600614908054-57142d1eec2b%3Fixid%3DMnwyMTY0MzZ8MHwxfHJhbmRvbXx8fHx8fHx8fDE2Mjc4MDc5MzY%26ixlib%3Drb-1.2.1&username=seonghojang95&name=Seongho+Jang&blur_hash=LKLy%5B%2AMd0L%3FG%3B0XSE2xDyC%25f%24zI%3B", @@ -184,9 +184,9 @@ researcher = {:ok, surveys} = Core.Repo.transaction(fn -> - for _ <- 1..survey_count do + for _ <- 1..questionnaire_count do %{ - type: :survey_tool, + type: :questionnaire_tool, promotion: %{ title: Faker.Lorem.sentence() <> " (survey)", subtitle: Faker.Lorem.sentence(), @@ -195,8 +195,8 @@ researcher = marks: ["vu"], plugin: "survey" }, - survey_tool: %{ - survey_url: Faker.Internet.url(), + questionnaire_tool: %{ + questionnaire_url: Faker.Internet.url(), # desktop_enabled: true, # phone_enabled: true, # tablet_enabled: true, @@ -250,7 +250,7 @@ Core.Repo.transaction( ) ) - if tool_type == :survey_tool do + if tool_type == :questionnaire_tool do participant_count = :random.uniform(tool.subject_count) for student <- Enum.take_random(students, participant_count) do diff --git a/core/frameworks/pixel/components/button.ex b/core/frameworks/pixel/components/button.ex index af713a4e5..ea1f4e81d 100644 --- a/core/frameworks/pixel/components/button.ex +++ b/core/frameworks/pixel/components/button.ex @@ -32,6 +32,7 @@ defmodule Frameworks.Pixel.Button do defp action_function(type) do case type do + :fake -> &Action.fake/1 :toggle -> &Action.toggle/1 :click -> &Action.click/1 :redirect -> &Action.redirect/1 diff --git a/core/frameworks/pixel/components/button_action.ex b/core/frameworks/pixel/components/button_action.ex index dda6999fe..2df1e98f3 100644 --- a/core/frameworks/pixel/components/button_action.ex +++ b/core/frameworks/pixel/components/button_action.ex @@ -1,6 +1,16 @@ defmodule Frameworks.Pixel.Button.Action do use CoreWeb, :html + slot(:inner_block, required: true) + + def fake(assigns) do + ~H""" +