replace sass with vanilla css

This commit is contained in:
Brent Schroeter 2025-12-18 12:55:01 -08:00
parent afba6497af
commit 4adbb84517
46 changed files with 1351 additions and 2000 deletions

1
.gitignore vendored
View file

@ -3,7 +3,6 @@ target
.env .env
.DS_Store .DS_Store
node_modules node_modules
css_dist
js_dist js_dist
pgdata pgdata
.vite .vite

View file

@ -25,7 +25,6 @@ USER 1000
WORKDIR /home/app WORKDIR /home/app
COPY --from=builder /app/target/release/phono-server /usr/local/bin COPY --from=builder /app/target/release/phono-server /usr/local/bin
COPY ./css_dist ./css_dist
COPY ./js_dist ./js_dist COPY ./js_dist ./js_dist
COPY ./static ./static COPY ./static ./static

252
deno.lock generated
View file

@ -14,7 +14,6 @@
"npm:@sveltejs/vite-plugin-svelte@^6.1.1": "6.1.1_svelte@5.38.1__acorn@8.15.0_vite@7.1.2__picomatch@4.0.3_sass-embedded@1.91.0", "npm:@sveltejs/vite-plugin-svelte@^6.1.1": "6.1.1_svelte@5.38.1__acorn@8.15.0_vite@7.1.2__picomatch@4.0.3_sass-embedded@1.91.0",
"npm:@tsconfig/svelte@^5.0.4": "5.0.4", "npm:@tsconfig/svelte@^5.0.4": "5.0.4",
"npm:date-fns@^4.1.0": "4.1.0", "npm:date-fns@^4.1.0": "4.1.0",
"npm:sass-embedded@^1.91.0": "1.91.0",
"npm:svelte-check@^4.3.1": "4.3.1_svelte@5.38.1__acorn@8.15.0_typescript@5.8.3", "npm:svelte-check@^4.3.1": "4.3.1_svelte@5.38.1__acorn@8.15.0_typescript@5.8.3",
"npm:svelte-language-server@~0.17.19": "0.17.19_prettier@3.3.3_svelte@4.2.20_typescript@5.9.2", "npm:svelte-language-server@~0.17.19": "0.17.19_prettier@3.3.3_svelte@4.2.20_typescript@5.9.2",
"npm:svelte@^5.37.3": "5.38.1_acorn@8.15.0", "npm:svelte@^5.37.3": "5.38.1_acorn@8.15.0",
@ -70,16 +69,10 @@
"@date-fns/utc@2.1.1": { "@date-fns/utc@2.1.1": {
"integrity": "sha512-SlJDfG6RPeEX8wEVv6ZB3kak4MmbtyiI2qX/5zuKdordbrhB/iaJ58GVMZgJ6P1sJaM1gMgENFYYeg1JWrCFrA==" "integrity": "sha512-SlJDfG6RPeEX8wEVv6ZB3kak4MmbtyiI2qX/5zuKdordbrhB/iaJ58GVMZgJ6P1sJaM1gMgENFYYeg1JWrCFrA=="
}, },
"@deno/vite-plugin@1.0.5_vite@7.1.2__picomatch@4.0.3": {
"integrity": "sha512-tLja5n4dyMhcze1NzvSs2iiriBymfBlDCZIrjMTxb9O2ru0gvmV6mn5oBD2teNw5Sd92cj3YJzKwsAs8tMJXlg==",
"dependencies": [
"vite@7.1.2_picomatch@4.0.3"
]
},
"@deno/vite-plugin@1.0.5_vite@7.1.2__picomatch@4.0.3_sass-embedded@1.91.0": { "@deno/vite-plugin@1.0.5_vite@7.1.2__picomatch@4.0.3_sass-embedded@1.91.0": {
"integrity": "sha512-tLja5n4dyMhcze1NzvSs2iiriBymfBlDCZIrjMTxb9O2ru0gvmV6mn5oBD2teNw5Sd92cj3YJzKwsAs8tMJXlg==", "integrity": "sha512-tLja5n4dyMhcze1NzvSs2iiriBymfBlDCZIrjMTxb9O2ru0gvmV6mn5oBD2teNw5Sd92cj3YJzKwsAs8tMJXlg==",
"dependencies": [ "dependencies": [
"vite@7.1.2_picomatch@4.0.3_sass-embedded@1.91.0" "vite"
] ]
}, },
"@emmetio/abbreviation@2.3.3": { "@emmetio/abbreviation@2.3.3": {
@ -97,261 +90,131 @@
"@emmetio/scanner@1.0.4": { "@emmetio/scanner@1.0.4": {
"integrity": "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA==" "integrity": "sha512-IqRuJtQff7YHHBk4G8YZ45uB9BaAGcwQeVzgj/zj8/UdOhtQpEIupUhSk8dys6spFIWVZVeK20CzGEnqR5SbqA=="
}, },
"@esbuild/aix-ppc64@0.25.8": {
"integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==",
"os": ["aix"],
"cpu": ["ppc64"]
},
"@esbuild/aix-ppc64@0.25.9": { "@esbuild/aix-ppc64@0.25.9": {
"integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
"os": ["aix"], "os": ["aix"],
"cpu": ["ppc64"] "cpu": ["ppc64"]
}, },
"@esbuild/android-arm64@0.25.8": {
"integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==",
"os": ["android"],
"cpu": ["arm64"]
},
"@esbuild/android-arm64@0.25.9": { "@esbuild/android-arm64@0.25.9": {
"integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
"os": ["android"], "os": ["android"],
"cpu": ["arm64"] "cpu": ["arm64"]
}, },
"@esbuild/android-arm@0.25.8": {
"integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==",
"os": ["android"],
"cpu": ["arm"]
},
"@esbuild/android-arm@0.25.9": { "@esbuild/android-arm@0.25.9": {
"integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
"os": ["android"], "os": ["android"],
"cpu": ["arm"] "cpu": ["arm"]
}, },
"@esbuild/android-x64@0.25.8": {
"integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==",
"os": ["android"],
"cpu": ["x64"]
},
"@esbuild/android-x64@0.25.9": { "@esbuild/android-x64@0.25.9": {
"integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
"os": ["android"], "os": ["android"],
"cpu": ["x64"] "cpu": ["x64"]
}, },
"@esbuild/darwin-arm64@0.25.8": {
"integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==",
"os": ["darwin"],
"cpu": ["arm64"]
},
"@esbuild/darwin-arm64@0.25.9": { "@esbuild/darwin-arm64@0.25.9": {
"integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
"os": ["darwin"], "os": ["darwin"],
"cpu": ["arm64"] "cpu": ["arm64"]
}, },
"@esbuild/darwin-x64@0.25.8": {
"integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==",
"os": ["darwin"],
"cpu": ["x64"]
},
"@esbuild/darwin-x64@0.25.9": { "@esbuild/darwin-x64@0.25.9": {
"integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
"os": ["darwin"], "os": ["darwin"],
"cpu": ["x64"] "cpu": ["x64"]
}, },
"@esbuild/freebsd-arm64@0.25.8": {
"integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==",
"os": ["freebsd"],
"cpu": ["arm64"]
},
"@esbuild/freebsd-arm64@0.25.9": { "@esbuild/freebsd-arm64@0.25.9": {
"integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
"os": ["freebsd"], "os": ["freebsd"],
"cpu": ["arm64"] "cpu": ["arm64"]
}, },
"@esbuild/freebsd-x64@0.25.8": {
"integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==",
"os": ["freebsd"],
"cpu": ["x64"]
},
"@esbuild/freebsd-x64@0.25.9": { "@esbuild/freebsd-x64@0.25.9": {
"integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
"os": ["freebsd"], "os": ["freebsd"],
"cpu": ["x64"] "cpu": ["x64"]
}, },
"@esbuild/linux-arm64@0.25.8": {
"integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==",
"os": ["linux"],
"cpu": ["arm64"]
},
"@esbuild/linux-arm64@0.25.9": { "@esbuild/linux-arm64@0.25.9": {
"integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
"os": ["linux"], "os": ["linux"],
"cpu": ["arm64"] "cpu": ["arm64"]
}, },
"@esbuild/linux-arm@0.25.8": {
"integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==",
"os": ["linux"],
"cpu": ["arm"]
},
"@esbuild/linux-arm@0.25.9": { "@esbuild/linux-arm@0.25.9": {
"integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
"os": ["linux"], "os": ["linux"],
"cpu": ["arm"] "cpu": ["arm"]
}, },
"@esbuild/linux-ia32@0.25.8": {
"integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==",
"os": ["linux"],
"cpu": ["ia32"]
},
"@esbuild/linux-ia32@0.25.9": { "@esbuild/linux-ia32@0.25.9": {
"integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
"os": ["linux"], "os": ["linux"],
"cpu": ["ia32"] "cpu": ["ia32"]
}, },
"@esbuild/linux-loong64@0.25.8": {
"integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==",
"os": ["linux"],
"cpu": ["loong64"]
},
"@esbuild/linux-loong64@0.25.9": { "@esbuild/linux-loong64@0.25.9": {
"integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
"os": ["linux"], "os": ["linux"],
"cpu": ["loong64"] "cpu": ["loong64"]
}, },
"@esbuild/linux-mips64el@0.25.8": {
"integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==",
"os": ["linux"],
"cpu": ["mips64el"]
},
"@esbuild/linux-mips64el@0.25.9": { "@esbuild/linux-mips64el@0.25.9": {
"integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
"os": ["linux"], "os": ["linux"],
"cpu": ["mips64el"] "cpu": ["mips64el"]
}, },
"@esbuild/linux-ppc64@0.25.8": {
"integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==",
"os": ["linux"],
"cpu": ["ppc64"]
},
"@esbuild/linux-ppc64@0.25.9": { "@esbuild/linux-ppc64@0.25.9": {
"integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
"os": ["linux"], "os": ["linux"],
"cpu": ["ppc64"] "cpu": ["ppc64"]
}, },
"@esbuild/linux-riscv64@0.25.8": {
"integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==",
"os": ["linux"],
"cpu": ["riscv64"]
},
"@esbuild/linux-riscv64@0.25.9": { "@esbuild/linux-riscv64@0.25.9": {
"integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
"os": ["linux"], "os": ["linux"],
"cpu": ["riscv64"] "cpu": ["riscv64"]
}, },
"@esbuild/linux-s390x@0.25.8": {
"integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==",
"os": ["linux"],
"cpu": ["s390x"]
},
"@esbuild/linux-s390x@0.25.9": { "@esbuild/linux-s390x@0.25.9": {
"integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
"os": ["linux"], "os": ["linux"],
"cpu": ["s390x"] "cpu": ["s390x"]
}, },
"@esbuild/linux-x64@0.25.8": {
"integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==",
"os": ["linux"],
"cpu": ["x64"]
},
"@esbuild/linux-x64@0.25.9": { "@esbuild/linux-x64@0.25.9": {
"integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
"os": ["linux"], "os": ["linux"],
"cpu": ["x64"] "cpu": ["x64"]
}, },
"@esbuild/netbsd-arm64@0.25.8": {
"integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==",
"os": ["netbsd"],
"cpu": ["arm64"]
},
"@esbuild/netbsd-arm64@0.25.9": { "@esbuild/netbsd-arm64@0.25.9": {
"integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
"os": ["netbsd"], "os": ["netbsd"],
"cpu": ["arm64"] "cpu": ["arm64"]
}, },
"@esbuild/netbsd-x64@0.25.8": {
"integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==",
"os": ["netbsd"],
"cpu": ["x64"]
},
"@esbuild/netbsd-x64@0.25.9": { "@esbuild/netbsd-x64@0.25.9": {
"integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
"os": ["netbsd"], "os": ["netbsd"],
"cpu": ["x64"] "cpu": ["x64"]
}, },
"@esbuild/openbsd-arm64@0.25.8": {
"integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==",
"os": ["openbsd"],
"cpu": ["arm64"]
},
"@esbuild/openbsd-arm64@0.25.9": { "@esbuild/openbsd-arm64@0.25.9": {
"integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
"os": ["openbsd"], "os": ["openbsd"],
"cpu": ["arm64"] "cpu": ["arm64"]
}, },
"@esbuild/openbsd-x64@0.25.8": {
"integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==",
"os": ["openbsd"],
"cpu": ["x64"]
},
"@esbuild/openbsd-x64@0.25.9": { "@esbuild/openbsd-x64@0.25.9": {
"integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
"os": ["openbsd"], "os": ["openbsd"],
"cpu": ["x64"] "cpu": ["x64"]
}, },
"@esbuild/openharmony-arm64@0.25.8": {
"integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==",
"os": ["openharmony"],
"cpu": ["arm64"]
},
"@esbuild/openharmony-arm64@0.25.9": { "@esbuild/openharmony-arm64@0.25.9": {
"integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
"os": ["openharmony"], "os": ["openharmony"],
"cpu": ["arm64"] "cpu": ["arm64"]
}, },
"@esbuild/sunos-x64@0.25.8": {
"integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==",
"os": ["sunos"],
"cpu": ["x64"]
},
"@esbuild/sunos-x64@0.25.9": { "@esbuild/sunos-x64@0.25.9": {
"integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
"os": ["sunos"], "os": ["sunos"],
"cpu": ["x64"] "cpu": ["x64"]
}, },
"@esbuild/win32-arm64@0.25.8": {
"integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==",
"os": ["win32"],
"cpu": ["arm64"]
},
"@esbuild/win32-arm64@0.25.9": { "@esbuild/win32-arm64@0.25.9": {
"integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
"os": ["win32"], "os": ["win32"],
"cpu": ["arm64"] "cpu": ["arm64"]
}, },
"@esbuild/win32-ia32@0.25.8": {
"integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==",
"os": ["win32"],
"cpu": ["ia32"]
},
"@esbuild/win32-ia32@0.25.9": { "@esbuild/win32-ia32@0.25.9": {
"integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
"os": ["win32"], "os": ["win32"],
"cpu": ["ia32"] "cpu": ["ia32"]
}, },
"@esbuild/win32-x64@0.25.8": {
"integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==",
"os": ["win32"],
"cpu": ["x64"]
},
"@esbuild/win32-x64@0.25.9": { "@esbuild/win32-x64@0.25.9": {
"integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
"os": ["win32"], "os": ["win32"],
@ -580,48 +443,26 @@
"acorn" "acorn"
] ]
}, },
"@sveltejs/vite-plugin-svelte-inspector@5.0.0_@sveltejs+vite-plugin-svelte@6.1.1__svelte@5.38.1___acorn@8.15.0__vite@7.1.2___picomatch@4.0.3_svelte@5.38.1__acorn@8.15.0_vite@7.1.2__picomatch@4.0.3": {
"integrity": "sha512-iwQ8Z4ET6ZFSt/gC+tVfcsSBHwsqc6RumSaiLUkAurW3BCpJam65cmHw0oOlDMTO0u+PZi9hilBRYN+LZNHTUQ==",
"dependencies": [
"@sveltejs/vite-plugin-svelte@6.1.1_svelte@5.38.1__acorn@8.15.0_vite@7.1.2__picomatch@4.0.3",
"debug",
"svelte@5.38.1_acorn@8.15.0",
"vite@7.1.2_picomatch@4.0.3"
]
},
"@sveltejs/vite-plugin-svelte-inspector@5.0.0_@sveltejs+vite-plugin-svelte@6.1.1__svelte@5.38.1___acorn@8.15.0__vite@7.1.2___picomatch@4.0.3_svelte@5.38.1__acorn@8.15.0_vite@7.1.2__picomatch@4.0.3_sass-embedded@1.91.0": { "@sveltejs/vite-plugin-svelte-inspector@5.0.0_@sveltejs+vite-plugin-svelte@6.1.1__svelte@5.38.1___acorn@8.15.0__vite@7.1.2___picomatch@4.0.3_svelte@5.38.1__acorn@8.15.0_vite@7.1.2__picomatch@4.0.3_sass-embedded@1.91.0": {
"integrity": "sha512-iwQ8Z4ET6ZFSt/gC+tVfcsSBHwsqc6RumSaiLUkAurW3BCpJam65cmHw0oOlDMTO0u+PZi9hilBRYN+LZNHTUQ==", "integrity": "sha512-iwQ8Z4ET6ZFSt/gC+tVfcsSBHwsqc6RumSaiLUkAurW3BCpJam65cmHw0oOlDMTO0u+PZi9hilBRYN+LZNHTUQ==",
"dependencies": [ "dependencies": [
"@sveltejs/vite-plugin-svelte@6.1.1_svelte@5.38.1__acorn@8.15.0_vite@7.1.2__picomatch@4.0.3_sass-embedded@1.91.0", "@sveltejs/vite-plugin-svelte",
"debug", "debug",
"svelte@5.38.1_acorn@8.15.0", "svelte@5.38.1_acorn@8.15.0",
"vite@7.1.2_picomatch@4.0.3_sass-embedded@1.91.0" "vite"
]
},
"@sveltejs/vite-plugin-svelte@6.1.1_svelte@5.38.1__acorn@8.15.0_vite@7.1.2__picomatch@4.0.3": {
"integrity": "sha512-vB0Vq47Js7C11L2JrwhncIAoDNkdKDPI500SjLSb34X48dDcsSH5JpLl0cHT0sfO997BrzAS6PKjiZEey/S0VQ==",
"dependencies": [
"@sveltejs/vite-plugin-svelte-inspector@5.0.0_@sveltejs+vite-plugin-svelte@6.1.1__svelte@5.38.1___acorn@8.15.0__vite@7.1.2___picomatch@4.0.3_svelte@5.38.1__acorn@8.15.0_vite@7.1.2__picomatch@4.0.3",
"debug",
"deepmerge",
"kleur",
"magic-string",
"svelte@5.38.1_acorn@8.15.0",
"vite@7.1.2_picomatch@4.0.3",
"vitefu@1.1.1_vite@7.1.2__picomatch@4.0.3"
] ]
}, },
"@sveltejs/vite-plugin-svelte@6.1.1_svelte@5.38.1__acorn@8.15.0_vite@7.1.2__picomatch@4.0.3_sass-embedded@1.91.0": { "@sveltejs/vite-plugin-svelte@6.1.1_svelte@5.38.1__acorn@8.15.0_vite@7.1.2__picomatch@4.0.3_sass-embedded@1.91.0": {
"integrity": "sha512-vB0Vq47Js7C11L2JrwhncIAoDNkdKDPI500SjLSb34X48dDcsSH5JpLl0cHT0sfO997BrzAS6PKjiZEey/S0VQ==", "integrity": "sha512-vB0Vq47Js7C11L2JrwhncIAoDNkdKDPI500SjLSb34X48dDcsSH5JpLl0cHT0sfO997BrzAS6PKjiZEey/S0VQ==",
"dependencies": [ "dependencies": [
"@sveltejs/vite-plugin-svelte-inspector@5.0.0_@sveltejs+vite-plugin-svelte@6.1.1__svelte@5.38.1___acorn@8.15.0__vite@7.1.2___picomatch@4.0.3_svelte@5.38.1__acorn@8.15.0_vite@7.1.2__picomatch@4.0.3_sass-embedded@1.91.0", "@sveltejs/vite-plugin-svelte-inspector",
"debug", "debug",
"deepmerge", "deepmerge",
"kleur", "kleur",
"magic-string", "magic-string",
"svelte@5.38.1_acorn@8.15.0", "svelte@5.38.1_acorn@8.15.0",
"vite@7.1.2_picomatch@4.0.3_sass-embedded@1.91.0", "vite",
"vitefu@1.1.1_vite@7.1.2__picomatch@4.0.3_sass-embedded@1.91.0" "vitefu"
] ]
}, },
"@tsconfig/svelte@5.0.4": { "@tsconfig/svelte@5.0.4": {
@ -721,32 +562,32 @@
"esbuild@0.25.9": { "esbuild@0.25.9": {
"integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
"optionalDependencies": [ "optionalDependencies": [
"@esbuild/aix-ppc64@0.25.9", "@esbuild/aix-ppc64",
"@esbuild/android-arm@0.25.9", "@esbuild/android-arm",
"@esbuild/android-arm64@0.25.9", "@esbuild/android-arm64",
"@esbuild/android-x64@0.25.9", "@esbuild/android-x64",
"@esbuild/darwin-arm64@0.25.9", "@esbuild/darwin-arm64",
"@esbuild/darwin-x64@0.25.9", "@esbuild/darwin-x64",
"@esbuild/freebsd-arm64@0.25.9", "@esbuild/freebsd-arm64",
"@esbuild/freebsd-x64@0.25.9", "@esbuild/freebsd-x64",
"@esbuild/linux-arm@0.25.9", "@esbuild/linux-arm",
"@esbuild/linux-arm64@0.25.9", "@esbuild/linux-arm64",
"@esbuild/linux-ia32@0.25.9", "@esbuild/linux-ia32",
"@esbuild/linux-loong64@0.25.9", "@esbuild/linux-loong64",
"@esbuild/linux-mips64el@0.25.9", "@esbuild/linux-mips64el",
"@esbuild/linux-ppc64@0.25.9", "@esbuild/linux-ppc64",
"@esbuild/linux-riscv64@0.25.9", "@esbuild/linux-riscv64",
"@esbuild/linux-s390x@0.25.9", "@esbuild/linux-s390x",
"@esbuild/linux-x64@0.25.9", "@esbuild/linux-x64",
"@esbuild/netbsd-arm64@0.25.9", "@esbuild/netbsd-arm64",
"@esbuild/netbsd-x64@0.25.9", "@esbuild/netbsd-x64",
"@esbuild/openbsd-arm64@0.25.9", "@esbuild/openbsd-arm64",
"@esbuild/openbsd-x64@0.25.9", "@esbuild/openbsd-x64",
"@esbuild/openharmony-arm64@0.25.9", "@esbuild/openharmony-arm64",
"@esbuild/sunos-x64@0.25.9", "@esbuild/sunos-x64",
"@esbuild/win32-arm64@0.25.9", "@esbuild/win32-arm64",
"@esbuild/win32-ia32@0.25.9", "@esbuild/win32-ia32",
"@esbuild/win32-x64@0.25.9" "@esbuild/win32-x64"
], ],
"scripts": true, "scripts": true,
"bin": true "bin": true
@ -1242,21 +1083,6 @@
"varint@6.0.0": { "varint@6.0.0": {
"integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==" "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg=="
}, },
"vite@7.1.2_picomatch@4.0.3": {
"integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==",
"dependencies": [
"esbuild",
"fdir",
"picomatch@4.0.3",
"postcss",
"rollup",
"tinyglobby"
],
"optionalDependencies": [
"fsevents"
],
"bin": true
},
"vite@7.1.2_picomatch@4.0.3_sass-embedded@1.91.0": { "vite@7.1.2_picomatch@4.0.3_sass-embedded@1.91.0": {
"integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==", "integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==",
"dependencies": [ "dependencies": [
@ -1276,22 +1102,13 @@
], ],
"bin": true "bin": true
}, },
"vitefu@1.1.1_vite@7.1.2__picomatch@4.0.3": {
"integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==",
"dependencies": [
"vite@7.1.2_picomatch@4.0.3"
],
"optionalPeers": [
"vite@7.1.2_picomatch@4.0.3"
]
},
"vitefu@1.1.1_vite@7.1.2__picomatch@4.0.3_sass-embedded@1.91.0": { "vitefu@1.1.1_vite@7.1.2__picomatch@4.0.3_sass-embedded@1.91.0": {
"integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==", "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==",
"dependencies": [ "dependencies": [
"vite@7.1.2_picomatch@4.0.3_sass-embedded@1.91.0" "vite"
], ],
"optionalPeers": [ "optionalPeers": [
"vite@7.1.2_picomatch@4.0.3_sass-embedded@1.91.0" "vite"
] ]
}, },
"vscode-css-languageservice@6.3.7": { "vscode-css-languageservice@6.3.7": {
@ -1364,7 +1181,6 @@
"npm:@sveltejs/vite-plugin-svelte@^6.1.1", "npm:@sveltejs/vite-plugin-svelte@^6.1.1",
"npm:@tsconfig/svelte@^5.0.4", "npm:@tsconfig/svelte@^5.0.4",
"npm:date-fns@^4.1.0", "npm:date-fns@^4.1.0",
"npm:sass-embedded@^1.91.0",
"npm:svelte-check@^4.3.1", "npm:svelte-check@^4.3.1",
"npm:svelte-language-server@~0.17.19", "npm:svelte-language-server@~0.17.19",
"npm:svelte@^5.37.3", "npm:svelte@^5.37.3",

View file

@ -3,7 +3,6 @@ deno = "latest"
rebar = "latest" rebar = "latest"
rust = { version = "nightly", components = "rust-analyzer,clippy,rustc-codegen-cranelift-preview" } rust = { version = "nightly", components = "rust-analyzer,clippy,rustc-codegen-cranelift-preview" }
watchexec = "latest" watchexec = "latest"
"github:sass/dart-sass" = "1.89.2"
"cargo:sqlx-cli" = "0.8.6" "cargo:sqlx-cli" = "0.8.6"
[tasks.dev-services] [tasks.dev-services]
@ -20,10 +19,6 @@ run = "deno run -A npm:vite build"
dir = "./svelte" dir = "./svelte"
sources = ["**/*.svelte.ts", "**/*.svelte"] sources = ["**/*.svelte.ts", "**/*.svelte"]
[tasks.build-css]
run = "sass sass/:css_dist/"
sources = ["**/*.scss"]
[tasks.docker-services] [tasks.docker-services]
dir = "./dev-services" dir = "./dev-services"
run = "docker compose up" run = "docker compose up"

View file

@ -15,7 +15,6 @@
"@deno/vite-plugin": "^1.0.5", "@deno/vite-plugin": "^1.0.5",
"@sveltejs/vite-plugin-svelte": "^6.1.1", "@sveltejs/vite-plugin-svelte": "^6.1.1",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"sass-embedded": "^1.91.0",
"svelte-language-server": "^0.17.19", "svelte-language-server": "^0.17.19",
"uuid": "^11.1.0", "uuid": "^11.1.0",
"vite": "^7.1.1", "vite": "^7.1.1",

View file

@ -71,31 +71,6 @@ pub(crate) fn new_router(app: App) -> Router<()> {
), ),
), ),
) )
.nest_service(
"/css_dist",
ServiceBuilder::new()
.layer(SetResponseHeaderLayer::if_not_present(
CACHE_CONTROL,
HeaderValue::from_static(if cfg!(debug_assertions) {
// Disable caching when developing locally.
"no-cache"
} else {
"max-age=120, stale-while-revalidate=86400"
}),
))
.service(
ServeDir::new("css_dist").not_found_service(
ServiceBuilder::new()
.layer(SetResponseHeaderLayer::if_not_present(
CACHE_CONTROL,
// Do not allow caching of paths if they return
// a 404 error.
HeaderValue::from_static("no-cache"),
))
.service(ServeFile::new("static/_404.html")),
),
),
)
.fallback_service( .fallback_service(
ServiceBuilder::new() ServiceBuilder::new()
.layer(SetResponseHeaderLayer::if_not_present( .layer(SetResponseHeaderLayer::if_not_present(

View file

@ -3,7 +3,9 @@
<head> <head>
<title>{% block title %}Phonograph{% endblock %}</title> <title>{% block title %}Phonograph{% endblock %}</title>
{% include "meta_tags.html" %} {% include "meta_tags.html" %}
<link rel="stylesheet" href="{{ settings.root_path }}/css_dist/main.css"> <link rel="stylesheet" href="{{ settings.root_path }}/modern-normalize.min.css">
<link rel="stylesheet" href="{{ settings.root_path }}/main.css">
<link rel="stylesheet" href="{{ settings.root_path }}/tabler-icons/webfont/tabler-icons.min.css">
<script type="module" src="{{ settings.root_path }}/js_dist/basic-dropdown.webc.mjs"></script> <script type="module" src="{{ settings.root_path }}/js_dist/basic-dropdown.webc.mjs"></script>
{%- block head_extras %}{% endblock -%} {%- block head_extras %}{% endblock -%}
</head> </head>

View file

@ -1,7 +1,7 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block head_extras %} {% block head_extras %}
<link rel="stylesheet" href="{{ settings.root_path }}/css_dist/viewer.css"> <link rel="stylesheet" href="{{ settings.root_path }}/portal-table.css">
<script type="module" src="{{ settings.root_path }}/js_dist/table-viewer.webc.mjs"></script> <script type="module" src="{{ settings.root_path }}/js_dist/table-viewer.webc.mjs"></script>
<script type="module" src="{{ settings.root_path }}/js_dist/filter-menu.webc.mjs"></script> <script type="module" src="{{ settings.root_path }}/js_dist/filter-menu.webc.mjs"></script>
{% endblock %} {% endblock %}
@ -10,7 +10,7 @@
<div class="page-grid"> <div class="page-grid">
<div class="page-grid__toolbar"> <div class="page-grid__toolbar">
<div class="page-grid__toolbar-utilities"> <div class="page-grid__toolbar-utilities">
<a class="button--secondary" href="settings" role="button"> <a class="button button--secondary" href="settings" role="button">
Portal Settings Portal Settings
</a> </a>
<filter-menu <filter-menu

View file

@ -1,13 +1,18 @@
<div class="role-display"> <div class="permission-badge">
<div class="role-display__resource">{{ self.relname }}</div> <div class="sr-only">{{ self.kind }} of {{ self.relname }}</div>
<div class="role-display__description"> <div aria-hidden="true" class="permission-badge__resource">{{ self.relname }}</div>
{%- match self.kind -%} <div
aria-hidden="true"
class="permission-badge__description"
style="--permission-badge-description-color: {% match self.kind -%}
{%- when RelPermissionKind::Owner -%} {%- when RelPermissionKind::Owner -%}
owner #fca
{%- when RelPermissionKind::Reader -%} {%- when RelPermissionKind::Reader -%}
reader #acf
{%- when RelPermissionKind::Writer -%} {%- when RelPermissionKind::Writer -%}
writer #fac
{%- endmatch -%} {%- endmatch %};"
>
{{ self.kind }}
</div> </div>
</div> </div>

View file

@ -5,7 +5,7 @@
<div class="page-grid__toolbar"> <div class="page-grid__toolbar">
<div class="page-grid__toolbar-utilities"> <div class="page-grid__toolbar-utilities">
<a <a
class="button--secondary" class="button button--secondary"
href="{{ navigator.portal_page() href="{{ navigator.portal_page()
.workspace_id(*portal.workspace_id) .workspace_id(*portal.workspace_id)
.rel_oid(*portal.class_oid) .rel_oid(*portal.class_oid)
@ -24,12 +24,24 @@
{{ workspace_nav | safe }} {{ workspace_nav | safe }}
</div> </div>
</div> </div>
<main class="page-grid__main padded--lg"> <main class="page-grid__main padded padded--lg">
<form method="post" action="update-name"> <form method="post" action="update-name">
<section> <section>
<h1>Portal Name</h1> <h1>Portal Name</h1>
<input type="text" name="name" value="{{ portal.name }}"> <input
<button class="button--primary" type="submit">Save</button> type="text"
autocomplete="off"
class="form__input"
data-1p-ignore
data-bwignore="true"
data-lpignore="true"
data-protonpass-ignore="true"
name="name"
value="{{ portal.name }}"
>
<div class="form__buttons">
<button class="button button--primary" type="submit">Save</button>
</div>
</section> </section>
</form> </form>
</main> </main>

View file

@ -10,18 +10,24 @@
{{ workspace_nav | safe }} {{ workspace_nav | safe }}
</div> </div>
</div> </div>
<main class="page-grid__main padded--lg"> <main class="page-grid__main padded padded--lg">
<form method="post" action="update-name"> <form method="post" action="update-name">
<section> <section>
<h1>Table Name</h1> <h1>Table Name</h1>
<input type="text" name="name" value="{{ rel.relname }}"> <input
<button class="button--primary" type="submit">Save</button> type="text"
</section> autocomplete="off"
</form> class="form__input"
<form method="post" action=""> data-1p-ignore
<section> data-bwignore="true"
<h1>Sharing</h1> data-lpignore="true"
<button class="button--primary" type="submit">Save</button> data-protonpass-ignore="true"
name="name"
value="{{ rel.relname }}"
>
<div class="form__buttons">
<button class="button button--primary" type="submit">Save</button>
</div>
</section> </section>
</form> </form>
</main> </main>

View file

@ -47,28 +47,23 @@
method="post" method="post"
> >
<!-- FIXME: CSRF --> <!-- FIXME: CSRF -->
<button class="button--secondary button--small" type="submit"> <button class="button button--secondary button--small" type="submit">
<i class="ti ti-database-plus"><div class="sr-only">Add table</div></i> <div class="sr-only">Add table</div>
<i class="ti ti-database-plus"></i>
</button> </button>
</form> </form>
</div> </div>
<menu class="workspace-nav__menu"> <menu class="workspace-nav__menu">
{%- for rel in relations %} {%- for rel in relations %}
<li> <li>
<collapsible-menu <div class="workspace-nav__menu-item">
class="workspace-nav__menu-item" <div class="workspace-nav__heading">
expanded=" <h3 class="text--data">{{ rel.name }}</h3>
{%- if let Some(NavLocation::Rel(rel_oid, _)) = current -%} <basic-dropdown>
{%- if rel_oid == &rel.oid -%} <span slot="button-contents">
true <span class="sr-only">Table menu</span>
{%- endif -%} <i class="ti ti-dots-vertical" aria-hidden="true"></i>
{%- endif -%} </span>
"
>
<div class="workspace-nav__heading" slot="summary">
<h3>{{ rel.name }}</h3>
<basic-dropdown button-class="button--secondary button--small" button-aria-label="Table menu">
<span slot="button-contents"><i class="ti ti-dots-vertical" aria-hidden="true"></i></span>
<menu slot="popover" class="basic-dropdown__menu"> <menu slot="popover" class="basic-dropdown__menu">
<li> <li>
<a <a
@ -84,10 +79,9 @@
</menu> </menu>
</basic-dropdown> </basic-dropdown>
</div> </div>
<menu class="workspace-nav__menu" slot="content"> <menu class="workspace-nav__menu">
<li class="workspace-nav__menu-item"> <li class="workspace-nav__menu-item">
<collapsible-menu class="workspace-nav__collapsible-menu"> <div class="workspace-nav__heading">
<div slot="summary" class="workspace-nav__heading">
<h4>Portals</h4> <h4>Portals</h4>
<form <form
action="{{ navigator.get_root_path() -}} action="{{ navigator.get_root_path() -}}
@ -97,7 +91,11 @@
method="post" method="post"
> >
<!-- FIXME: CSRF --> <!-- FIXME: CSRF -->
<button aria-label="Add portal" class="workspace-nav__aux-button" type="submit"> <button
class="button button--secondary button--small"
type="submit"
>
<div class="sr-only">Add portal</div>
<i class="ti ti-table-plus"></i> <i class="ti ti-table-plus"></i>
</button> </button>
</form> </form>
@ -117,7 +115,7 @@
/r/{{ rel.oid.0 -}} /r/{{ rel.oid.0 -}}
/p/{{ portal.id.simple() -}} /p/{{ portal.id.simple() -}}
" "
class="workspace-nav__menu-link" class="workspace-nav__menu-link text--data"
> >
{{ portal.name }} {{ portal.name }}
</a> </a>
@ -136,28 +134,15 @@
Portal settings Portal settings
</a> </a>
</li> </li>
<li>
<a
href="{{ navigator.get_root_path() -}}
/w/{{ workspace.id.simple() -}}
/r/{{ rel.oid.0 -}}
/p/{{ portal.id.simple() -}}
/form/"
role="button"
>
Form
</a>
</li>
</menu> </menu>
</basic-dropdown> </basic-dropdown>
</div> </div>
</li> </li>
{% endfor %} {% endfor %}
</menu> </menu>
</collapsible-menu>
</li> </li>
</menu> </menu>
</collapsible-menu> </div>
</li> </li>
{% endfor -%} {% endfor -%}
</menu> </menu>

View file

@ -1,16 +1,18 @@
{% for perm in current_perms.clone() %} <div class="permissions-list">
{{ perm | safe }} {% for perm in current_perms.clone() %}
{% endfor %} {{ perm | safe }}
<button {% endfor %}
<button
class="button button--secondary button--small" class="button button--secondary button--small"
popovertarget="permissions-editor-{{ target }}" popovertarget="permissions-editor-{{ target }}"
popovertargetaction="toggle" popovertargetaction="toggle"
type="button" type="button"
> >
Edit Edit
</button> </button>
</div>
<dialog <dialog
class="dialog padded--lg" class="dialog padded padded--lg"
id="permissions-editor-{{ target }}" id="permissions-editor-{{ target }}"
popover="auto" popover="auto"
> >
@ -82,7 +84,7 @@
<input type="hidden" name="{{ k }}" value="{{ v }}"> <input type="hidden" name="{{ k }}" value="{{ v }}">
{% endfor %} {% endfor %}
<button <button
class="button--primary" class="button button--primary"
style="margin-top: 16px;" style="margin-top: 16px;"
type="submit" type="submit"
> >

View file

@ -24,7 +24,7 @@
{{ workspace_nav | safe }} {{ workspace_nav | safe }}
</div> </div>
</div> </div>
<main class="page-grid__main padded--lg"> <main class="page-grid__main padded padded--lg">
<table class="table"> <table class="table">
<thead> <thead>
<tr> <tr>

View file

@ -10,21 +10,35 @@
{{ workspace_nav | safe }} {{ workspace_nav | safe }}
</div> </div>
</div> </div>
<main class="page-grid__main padded--lg"> <main class="page-grid__main padded padded--lg">
<form method="post" action="update-name"> <form method="post" action="update-name">
<section> <section>
<h1>Workspace Name</h1> <h1>Workspace Name</h1>
<input type="text" name="name" value="{{ workspace.display_name }}"> <input
<button class="button--primary" type="submit">Save</button> type="text"
autocomplete="off"
class="form__input"
data-1p-ignore
data-bwignore="true"
data-lpignore="true"
data-protonpass-ignore="true"
name="name"
value="{{ workspace.display_name }}"
>
<div class="form__buttons">
<button class="button button--primary" type="submit">Save</button>
</div>
</section> </section>
</form> </form>
<section> <section>
<h1>Sharing</h1> <h1>Sharing</h1>
<div class="form__buttons">
<button class="button button--primary" popovertarget="invite-dialog" type="button"> <button class="button button--primary" popovertarget="invite-dialog" type="button">
Invite Invite
</button> </button>
</div>
<dialog <dialog
class="dialog padded--lg" class="dialog padded padded--lg"
id="invite-dialog" id="invite-dialog"
popover="auto" popover="auto"
> >
@ -51,7 +65,7 @@
</div> </div>
</form> </form>
</dialog> </dialog>
<table class="table"> <table class="table" style="margin-top: var(--default-padding);">
<thead> <thead>
<tr> <tr>
<th scope="col">Email</th> <th scope="col">Email</th>
@ -63,7 +77,7 @@
{% for permissions_editor in collaborators %} {% for permissions_editor in collaborators %}
<tr> <tr>
{% let collaborator = User::try_from(permissions_editor.target.clone())? %} {% let collaborator = User::try_from(permissions_editor.target.clone())? %}
<td>{{ collaborator.email }}</td> <td class="text--data">{{ collaborator.email }}</td>
<td> <td>
{{ permissions_editor | safe }} {{ permissions_editor | safe }}
</td> </td>
@ -80,7 +94,9 @@
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
<button class="button--primary" type="submit">Save</button> <div class="form__buttons">
<button class="button button--primary" type="submit">Save</button>
</div>
</section> </section>
</main> </main>
</div> </div>

View file

@ -1,40 +0,0 @@
@use 'globals';
.basic-dropdown {
&__button {
anchor-name: --anchor-button;
}
&__popover {
&:popover-open {
@include globals.popover;
padding: 0;
position: absolute;
position-anchor: --anchor-button;
top: anchor(bottom);
}
}
&__menu {
list-style-type: none;
margin: 0;
padding: 8px 0;
& > li {
padding: 0;
&:hover {
background: #0001;
}
& > button, & > [role="button"] {
@include globals.reset-button;
@include globals.reset-anchor;
display: block;
padding: 8px 16px;
}
}
}
}

View file

@ -1,30 +0,0 @@
@use 'globals';
@use 'viewer-shared';
.field-adder {
&__container {
align-items: stretch;
display: flex;
}
&__header-lookalike {
@include viewer-shared.th;
border-bottom-style: dashed;
border-right-style: dashed;
}
&__label-input {
@include globals.reset-input;
}
&__popover:popover-open {
@include globals.popover;
padding: 1rem;
}
&__summary-buttons {
align-items: center;
display: flex;
}
}

View file

@ -1,57 +0,0 @@
@use 'globals';
$section-gap: 1.5rem;
$label-gap: 0.5rem;
$button-gap: 0.25rem;
.form-section {
&__heading {
margin: 0;
font-size: 1rem;
font-weight: 700;
}
&__label {
display: block;
font-weight: 600;
margin-top: $section-gap;
}
&__input {
display: block;
margin-top: $label-gap;
font-family: globals.$font-family-data;
&--text {
@include globals.rounded;
border: globals.$default-border;
padding: 0.5rem;
}
}
}
.form-buttons {
display: flex;
margin-top: $section-gap;
justify-content: flex-end;
&__button {
margin: 0 $button-gap;
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
&--cancel {
@include globals.button-clear;
}
&--submit {
@include globals.button-primary;
}
}
}

View file

@ -1,153 +0,0 @@
@use 'sass:color';
$button-primary-background: #fc0;
$button-primary-color: #000;
$button-shadow: 0 0.15rem 0.15rem #3331;
$default-border-color: #ccc;
$default-border: solid 1px $default-border-color;
$font-family-default: 'Funnel Sans', 'Open Sans', 'Helvetica Neue', Arial, sans-serif;
$font-family-data: Menlo, 'Courier New', 'Open Sans', 'Helvetica Neue', Arial, sans-serif;
$font-family-mono: Menlo, 'Courier New', Courier, mono;
$popover-border: $default-border;
$popover-shadow: 0 0.5rem 0.5rem #3333;
$border-radius-rounded-sm: 0.25rem;
$border-radius-rounded: 0.5rem;
$link-color: #069;
$notice-color-info: #39d;
$hover-lightness-scale-factor: -5%;
@mixin reset-button {
appearance: none;
background: none;
border: none;
padding: 0;
box-sizing: border-box;
cursor: pointer;
font-family: inherit;
font-size: inherit;
font-weight: inherit;
}
@mixin button-base {
@include reset-button;
@include rounded;
box-shadow: $button-shadow;
font-family: $font-family-mono;
font-weight: 500;
padding: 0.5rem 1rem;
transition: background 0.2s ease;
}
@mixin button-primary {
@include button-base;
background: $button-primary-background;
border: solid 1px color.scale(
$button-primary-background,
$lightness: -5%,
$space: oklch
);
color: $button-primary-color;
&:hover {
background: color.scale(
$button-primary-background,
$lightness: $hover-lightness-scale-factor,
$space: oklch
);
border-color: color.scale(
$button-primary-background,
$lightness: -10%,
$space: oklch
);
}
}
@mixin button-outline {
@include button-base;
background: $button-primary-color;
border: solid 1px color.scale(
$button-primary-background,
$lightness: -5%,
$space: oklch
);
color: $button-primary-background;
&:hover {
border-color: color.scale(
$button-primary-background,
$lightness: -10%,
$space: oklch
);
}
}
@mixin button-secondary {
@include button-base;
background: #fff;
color: #000;
border: $default-border;
&:hover {
border-color: color.scale(
$default-border-color,
$lightness: $hover-lightness-scale-factor,
$space: oklch
);
}
}
@mixin button-clear {
@include button-base;
box-shadow: none;
&:hover {
background: #0000001f;
}
}
@mixin button-small {
padding: 4px 8px;
font-size: 0.9rem;
}
@mixin reset-input {
appearance: none;
background: none;
border: none;
box-sizing: border-box;
font-family: inherit;
font-size: inherit;
font-weight: inherit;
outline: none;
}
@mixin reset-anchor {
color: inherit;
text-decoration: none;
}
@mixin rounded-sm {
border-radius: $border-radius-rounded-sm;
}
@mixin rounded {
border-radius: $border-radius-rounded;
}
@mixin popover {
@include rounded;
inset: unset;
border: $popover-border;
margin: 0;
margin-top: 4px;
padding: 0;
position: relative;
display: block;
background: #fff;
box-shadow: $popover-shadow;
}

View file

@ -1,121 +0,0 @@
@use 'globals';
$background-color: #fff;
$tab-button-color-active: #0001;
.container-positioner {
position: fixed;
bottom: 2rem;
display: flex;
justify-content: center;
align-items: flex-end;
overflow: visible;
width: 100%;
height: 0;
}
.container {
display: grid;
grid-template-columns: max-content max-content 1rem max-content;
filter: drop-shadow(globals.$popover-shadow);
}
.tab-box {
@include globals.rounded;
height: 3rem;
display: flex;
align-items: center;
list-style-type: none;
padding: 0.5rem;
margin: 0;
border: globals.$popover-border;
border-right: none;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
background: $background-color;
grid-row: 2;
&__button {
@include globals.reset-button;
@include globals.rounded-sm;
display: flex;
align-items: center;
padding: 0.25rem 0.5rem;
cursor: pointer;
height: 2rem;
&--active {
background: $tab-button-color-active;
}
}
}
.control-bar {
@include globals.rounded;
height: 3rem;
flex-shrink: 0;
border: globals.$popover-border;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
overflow: hidden;
width: 40rem;
grid-row: 2;
background: $background-color;
&--open {
border-top-right-radius: 0;
}
}
.control-buttons {
height: 3rem;
gird-row: 2;
grid-column: 4;
height: 100%;
}
.control-panel-positioner {
grid-template-columns: subgrid;
grid-column: 2;
grid-row: 1;
position: relative;
overflow: visible;
/* Flexbox positioning is required for Safari */
display: flex;
align-items: flex-end;
anchor-name: --control-bar;
}
.control-panel__container:popover-open {
@include globals.rounded;
inset: unset;
border: globals.$popover-border;
border-bottom: none;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
margin: 0;
position: fixed;
display: block;
width: 40rem;
/* Anchor positioning is required for Chromium */
position-anchor: --control-bar;
position-area: top;
padding: 0;
background: $background-color;
box-shadow: globals.$popover-shadow;
/* Clip drop shadow */
clip-path: polygon(
-100% -100%,
200% -100%,
200% 200%,
100% 200%,
100% 100%,
-100% 100%
);
}
.control-panel {
padding: 0.5rem;
overflow: auto;
max-height: 8rem;
}

View file

@ -1,16 +0,0 @@
@use 'globals';
@mixin th {
border: globals.$default-border;
border-top: none;
font-family: 'Funnel Sans';
font-weight: bolder;
background: #0001;
height: 100%; /* css hack to make percentage based cell heights work */
padding: 0.25rem 0.5rem;
text-align: left;
&:first-child {
border-left: none;
}
}

View file

@ -1,34 +0,0 @@
@use 'globals';
.cell__container {
display: block;
width: 100%;
height: 100%;
border: solid 2px transparent;
&--selected {
border-color: #07f;
}
}
.cell__content {
font-family: globals.$font-family-data;
max-width: 100%;
&--padded {
padding: 0.25rem;
}
&--null {
opacity: 0.5;
font-style: oblique;
text-align: center;
}
&--uuid {
font-family: globals.$font-family-mono;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}

View file

@ -1,20 +0,0 @@
@use 'globals';
.collapsible-menu {
&__summary {
@include globals.reset-button;
}
&__content {
overflow: hidden;
max-height: 0;
transition: max-height 0.3s ease-in;
padding-left: 0.5rem;
&--expanded {
// todo: adjust max-height dynamically based on content
max-height: 40rem;
transition: max-height 0.3s ease-out;
}
}
}

View file

@ -1,93 +0,0 @@
@use 'sass:color';
@use 'globals';
.expression-editor {
&__container {
@include globals.rounded;
background: #eee;
display: flex;
}
&__sidebar {
display: grid;
grid-template:
'padding-top' 1fr
'operator-selector' max-content
'actions' minmax(max-content, 1fr);
}
&__main {
@include globals.rounded;
background: #fff;
border: globals.$default-border;
flex: 1;
padding: 0.5rem;
}
&__action-button {
padding: 0.5rem;
svg path {
fill: currentColor;
}
}
&__params {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
}
.expression-selector {
&__container {
grid-area: operator-selector;
}
&__expression-button {
@include globals.button-clear;
align-items: center;
display: flex;
justify-content: center;
height: 2.5rem;
padding: 0;
width: 2.5rem;
svg path {
fill: currentColor;
}
}
&__popover {
&:popover-open {
@include globals.rounded;
inset: unset;
top: anchor(bottom);
border: globals.$popover-border;
margin: 0;
margin-top: 0.25rem;
position: absolute;
display: flex;
flex-direction: column;
padding: 0;
background: #fff;
box-shadow: globals.$popover-shadow;
}
}
&__section {
align-items: center;
display: grid;
grid-template-columns: repeat(3, 1fr);
justify-content: center;
list-style-type: none;
margin: 1rem;
padding: 0;
}
&__li {
align-items: center;
display: flex;
justify-content: center;
}
}

View file

@ -1,48 +0,0 @@
@use '../globals';
@use '../forms';
@use '../viewer-shared';
:host {
height: 100%;
}
.expander__button {
@include globals.reset-button;
width: 100%;
height: 100%;
}
.header {
@include viewer-shared.th;
border-right-style: dashed;
border-bottom-style: dashed;
border-left: none;
display: flex;
&__input {
appearance: none;
background: none;
border: none;
outline: none;
font-weight: inherit;
}
}
.config-popover {
&__container {
@include globals.rounded;
font-family: globals.$font-family-default;
position: fixed;
inset: unset;
margin: 0;
width: 20rem;
padding: 1rem;
font-weight: normal;
border: globals.$popover-border;
filter: drop-shadow(globals.$popover-shadow);
&:popover-open {
display: block;
}
}
}

View file

@ -1,10 +0,0 @@
@use 'globals';
.phono-form{
&__container {
margin: 0 auto;
max-width: 48rem;
padding: 2rem;
position: relative;
}
}

View file

@ -1,212 +0,0 @@
@use 'sass:color';
@use 'basic-dropdown';
@use 'globals';
@use 'modern-normalize';
@use 'forms';
@use 'collapsible_menu';
@use 'condition-editor';
@use 'tabler-icons' with (
$ti-font-path: '../tabler-icons/webfont/fonts'
);
@use 'workspace-nav';
html {
font-family: globals.$font-family-default;
}
button, input[type="submit"] {
@include globals.reset-button;
}
@font-face {
font-family: "Averia Serif Libre";
src: url("../averia_serif_libre/averia_serif_libre_regular.ttf");
}
@font-face {
font-family: "Averia Serif Libre";
src: url("../averia_serif_libre/averia_serif_libre_bold.ttf");
font-weight: 700;
}
@font-face {
font-family: "Averia Serif Libre";
src: url("../averia_serif_libre/averia_serif_libre_light.ttf");
font-weight: 300;
}
@font-face {
font-family: "Funnel Sans";
src: url("../funnel_sans/funnel_sans_variable.ttf");
}
@view-transitions {
navigation: auto;
}
// https://css-tricks.com/inclusively-hidden/
.sr-only:not(:focus):not(:active) {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
.button {
&--primary {
@include globals.reset-anchor;
@include globals.button-primary;
}
&--secondary {
@include globals.reset-anchor;
@include globals.button-secondary;
}
&--clear {
@include globals.reset-anchor;
@include globals.button-clear;
}
&--small {
@include globals.reset-anchor;
@include globals.button-small;
}
}
.padded {
padding: 8px;
&--lg {
padding: 32px;
}
}
.page-grid {
height: 100vh;
width: 100vw;
display: grid;
grid-template:
'sidebar toolbar' 4rem
'sidebar main' 1fr / max-content 1fr;
&__toolbar {
align-items: center;
border-bottom: globals.$default-border;
display: grid;
grid-area: toolbar;
grid-template:
'utilities user' 1fr / 1fr max-content;
}
&__toolbar-utilities {
align-items: center;
display: flex;
gap: 12px;
grid-area: utilities;
justify-content: flex-start;
padding: 0 12px;
}
&__toolbar-user {
align-items: center;
display: flex;
gap: 12px;
grid-area: user;
justify-content: flex-end;
padding: 0 12px;
}
&__sidebar {
grid-area: sidebar;
width: 15rem;
max-height: 100vh;
overflow: auto;
border-right: globals.$default-border;
}
&__main {
grid-area: main;
overflow: auto;
}
}
.toolbar-item {
flex: 0;
}
.section {
padding: 1rem 2rem;
}
.notice {
@include globals.rounded;
margin: 1rem 0rem;
padding: 1rem;
max-width: 40rem;
&--info {
border: solid 1px globals.$notice-color-info;
background: color.scale(globals.$notice-color-info, $lightness: 90%, $space: hsl);
color: color.scale(globals.$notice-color-info, $lightness: -80%, $space: hsl);
}
}
.role-tree {
font-family: globals.$font-family-data;
&--no-inherit {
opacity: 0.6;
font-style: italic;
}
&__branches {
border-left: solid 1px #000;
list-style-type: none;
margin: 0;
margin-top: 0.25rem;
padding: 0;
}
&__branch {
padding-top: 0.25rem;
padding-left: 1rem;
}
}
.table {
border-collapse: collapse;
th, td {
border: globals.$default-border;
padding: 8px;
}
th {
background: #eee;
}
&__message {
opacity: 0.5;
text-align: center;
}
}
.phono-popover:popover-open {
@include globals.popover;
}
.dialog:popover-open, .dialog:open {
@include globals.rounded;
background: #fff;
border: globals.$popover-border;
box-shadow: globals.$popover-shadow;
display: block;
max-height: 90vh;
overflow: auto;
}

View file

@ -1 +0,0 @@
../static/tabler-icons/webfont/tabler-icons.scss

View file

@ -1,384 +0,0 @@
@use 'globals';
@use 'sass:color';
@use 'condition-editor';
@use 'field-adder';
$table-border-color: #ccc;
.lens-grid {
display: grid;
grid-template:
'table' 1fr
'editor' max-content;
height: 100%;
width: 100%;
}
.lens-table {
display: grid;
grid-area: table;
grid-template:
'headers' max-content
'main' 1fr
'inserter' max-content;
height: 100%;
outline: none;
overflow: auto;
width: 100%;
&__headers {
align-items: stretch;
display: flex;
grid-area: headers;
// Ensure that there will be enough space on the right for popovers to
// render without overflowing the container.
padding-right: 480px;
}
&__main {
grid-area: main;
overflow-y: auto;
}
&__row {
align-items: stretch;
display: flex;
height: 2.25rem;
}
}
.lens-cell {
align-items: stretch;
border: solid 1px $table-border-color;
border-left: none;
border-top: none;
display: flex;
flex: none;
padding: 0;
&:focus {
outline: none;
}
&--insertable {
border-style: dashed;
}
&__container {
align-items: center;
display: flex;
flex: none;
user-select: none;
width: 100%;
&--selected {
background: #07f3;
}
&--cursor {
outline: 3px solid #37f;
outline-offset: -2px;
}
}
&__content {
flex: 1;
font-family: globals.$font-family-data;
&--dropdown {
overflow: hidden;
padding: 0 8px;
}
&--numeric {
overflow: hidden;
text-align: right;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 8px;
}
&--text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 8px;
}
&--timestamp {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 8px;
}
&--uuid {
font-family: globals.$font-family-mono;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 0.5rem;
}
&--null {
align-items: center;
color: color.scale(#000, $lightness: 65%, $space: hsl);
display: flex;
font-family: globals.$font-family-data;
font-style: oblique;
justify-content: center;
padding: 0 0.25rem;
svg path {
fill: currentColor;
}
}
}
&__notice {
align-items: center;
color: color.scale(#000, $lightness: 50%, $space: hsl);
display: flex;
padding: 0 0.25rem;
svg path {
fill: currentColor;
}
}
}
.field-header {
&__container {
align-items: center;
background: #0001;
border: solid 1px #ccc;
border-top: none;
border-left: none;
display: flex;
flex: none;
font-family: globals.$font-family-data;
justify-content: space-between;
padding: 0.25rem;
text-align: left;
}
&__label {
padding: 0.25rem;
}
&__type-indicator {
@include globals.button-clear;
align-items: center;
display: flex;
height: 2rem;
justify-content: center;
padding: 0;
width: 2rem;
svg path {
fill: currentColor;
}
}
&__popover {
&:popover-open {
@include globals.popover;
left: anchor(left);
top: anchor(bottom);
padding: 1rem;
position: absolute;
}
}
}
.lens-inserter {
grid-area: inserter;
margin-bottom: 2rem;
&__help {
font-size: 1rem;
font-weight: lighter;
margin: 8px;
opacity: 0.5;
}
&__main {
align-items: stretch;
display: flex;
justify-items: flex-start;
}
&__rows {
.lens-cell {
border: dashed 1px $table-border-color;
border-left: none;
&:last-child {
border-right: none;
}
}
.lens-table__row:first-child .lens-table__cell {
border-top: dashed 1px $table-border-color;
}
}
&__submit {
@include globals.reset-button;
align-items: center;
border: dashed 1px globals.$button-primary-background;
border-bottom-right-radius: globals.$border-radius-rounded-sm;
border-top-right-radius: globals.$border-radius-rounded-sm;
color: globals.$button-primary-background;
display: flex;
justify-content: center;
padding: 0 1rem;
svg path {
fill: currentColor;
}
}
}
.table-viewer__datum-editor {
border-top: globals.$default-border;
display: flex;
grid-area: editor;
height: 6rem;
}
.dropdown-option-badge {
background: #ccc;
border-radius: 999px;
display: block;
width: max-content;
&:not(:has(button)) {
padding: 6px 12px;
}
&--red {
background: #f99;
color: color.scale(#f99, $lightness: -66%, $space: oklch);
}
&--orange {
background: color.adjust(#f99, $hue: 50deg, $space: oklch);
color: color.scale(color.adjust(#f99, $hue: 50deg, $space: oklch), $lightness: -66%, $space: oklch);
}
&--yellow {
background: color.adjust(#f99, $hue: 100deg, $space: oklch);
color: color.scale(color.adjust(#f99, $hue: 100deg, $space: oklch), $lightness: -66%, $space: oklch);
}
&--green {
background: color.adjust(#f99, $hue: 150deg, $space: oklch);
color: color.scale(color.adjust(#f99, $hue: 150deg, $space: oklch), $lightness: -66%, $space: oklch);
}
&--blue {
background: color.adjust(#f99, $hue: 200deg, $space: oklch);
color: color.scale(color.adjust(#f99, $hue: 200deg, $space: oklch), $lightness: -66%, $space: oklch);
}
&--indigo {
background: color.adjust(#f99, $hue: 250deg, $space: oklch);
color: color.scale(color.adjust(#f99, $hue: 250deg, $space: oklch), $lightness: -66%, $space: oklch);
}
&--violet {
background: color.adjust(#f99, $hue: 300deg, $space: oklch);
color: color.scale(color.adjust(#f99, $hue: 300deg, $space: oklch), $lightness: -66%, $space: oklch);
}
button {
@include globals.button-clear;
color: inherit;
padding: 6px 12px;
}
}
.datum-editor {
&__container {
border-left: solid 4px transparent;
display: grid;
flex: 1;
grid-template: 'type-selector type-selector' max-content
'null-control value-control' max-content
'helpers helpers' auto / max-content auto;
&:has(:focus) {
border-left-color: #07f;
}
&--incomplete {
border-left-color: #f33;
}
}
&__type-selector {
grid-area: type-selector;
}
&__null-control {
@include globals.reset_button;
align-self: start;
grid-area: null-control;
padding: 0.75rem;
&--disabled {
opacity: 0.75;
}
}
&__text-input {
@include globals.reset_input;
grid-area: value-control;
font-family: globals.$font-family-data;
padding: 0.75rem 0.5rem;
}
&__timestamp-inputs {
align-items: center;
display: flex;
grid-area: value-control;
justify-content: start;
input {
@include globals.reset_input;
}
}
&__helpers {
grid-area: helpers;
overflow: auto;
}
&__dropdown-options {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 8px;
justify-content: flex-start;
margin: 0;
padding: 0 8px;
}
}
.toolbar-popover {
&:popover-open {
@include globals.rounded;
inset: unset;
border: globals.$popover-border;
margin: 0;
margin-top: 0.25rem;
position: fixed;
display: block;
width: 24rem;
padding: 0.5rem;
background: #fff;
box-shadow: globals.$popover-shadow;
}
}

View file

@ -1,35 +0,0 @@
@use 'globals';
@use 'hoverbar';
.text-editor {
width: 100%;
height: 100%;
display: flex;
align-items: stretch;
&__input {
@include globals.reset-input;
padding: 0.5rem;
font-family: globals.$font-family-data;
flex: 1;
}
}
.toggle {
&__container {
display: flex;
align-items: center;
}
&__checkbox {
margin-right: 0.25rem;
}
&__label {
margin-right: 0.5rem;
&--disabled {
opacity: 0.5;
}
}
}

View file

@ -1,80 +0,0 @@
@use 'globals';
$background-current-item: #0001;
.workspace-nav {
& h1, h2, h3, h4, h5, h6 {
margin: 0;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
& h1 {
font-size: 1.5rem;
}
& h2 {
font-size: 1.25rem;
}
& h3 {
font-size: 1.125rem;
}
& h4, h5, h6 {
font-size: 1rem;
}
&__section {
margin-top: 16px;
}
&__menu {
list-style-type: none;
padding: 0;
margin: 0;
margin-top: 16px;
}
&__heading {
align-items: center;
display: flex;
font-size: inherit;
justify-content: space-between;
margin: 8px;
}
&__aux-button {
@include globals.button-secondary;
@include globals.button-small;
text-decoration: none;
}
&__menu-item {
display: block;
margin-left: 8px;
padding: 0;
}
&__menu-leaf {
@include globals.rounded-sm;
align-items: center;
display: flex;
justify-content: space-between;
padding: 0 8px;
&--current, &:hover {
background: $background-current-item;
}
}
&__menu-link {
color: globals.$link-color;
flex: 1;
padding: 12px 0;
text-decoration: none;
}
}

View file

@ -0,0 +1,78 @@
.expression-editor__container {
background: #eee;
border-radius: var(--default-border-radius--rounded);
display: flex;
}
.expression-editor__sidebar {
display: grid;
grid-template:
'padding-top' 1fr
'operator-selector' max-content
'actions' minmax(max-content, 1fr);
}
.expression-editor__main {
background: #fff;
border-radius: var(--default-border-radius--rounded);
border: solid 1px var(--default-border-color);
flex: 1;
padding: var(--default-padding);
}
.expression-editor__action-button {
padding: var(--default-padding);
svg path {
fill: currentColor;
}
}
.expression-editor__params {
display: flex;
flex-direction: column;
gap: var(--default-padding);
}
.expression-selector {
grid-area: operator-selector;
}
.expression-selector__expression-button {
align-items: center;
display: flex;
justify-content: center;
height: 2.5rem;
padding: 0;
width: 2.5rem;
svg path {
fill: currentColor;
}
}
.expression-selector__popover:popover-open {
top: anchor(bottom);
margin-top: 0.25rem;
position: absolute;
display: flex;
flex-direction: column;
padding: 0;
background: #fff;
}
.expression-selector__section {
align-items: center;
display: grid;
grid-template-columns: repeat(3, 1fr);
justify-content: center;
list-style-type: none;
margin: var(--default-padding);
padding: 0;
}
.expression-selector__li {
align-items: center;
display: flex;
justify-content: center;
}

480
static/main.css Normal file
View file

@ -0,0 +1,480 @@
/*
@use 'forms';
@use 'condition-editor';
*/
/* ======== Theming ======== */
:root {
--accent-color: #fc0;
--default-border-color: #ccc;
--default-border-radius--rounded: 8px;
--default-border-radius--rounded-sm: 4px;
--default-font-family:
'Funnel Sans',
'Open Sans',
'Helvetica Neue',
Arial,
sans-serif;
--default-font-family--data:
Menlo,
'Courier New',
'Open Sans',
'Helvetica Neue',
Arial,
sans-serif;
--default-font-family--mono:
Menlo,
'Courier New',
Courier,
mono;
--default-padding: 16px;
--default-padding--xs: 4px;
--default-padding--sm: 8px;
--default-padding--lg: 32px;
--a-color: #069;
--button-background--primary: var(--accent-color);
--button-background--secondary: #fff;
--button-border-color--primary: oklch(from var(--button-background--primary) calc(l * 0.9) c h);
--button-border-color--secondary: oklch(from var(--button-background--secondary) calc(l * 0.85) c h);
--button-border-radius: var(--default-border-radius--rounded);
--button-color--primary: #000;
--button-color--secondary: #000;
--button-font-family: var(--default-font-family);
--button-font-size: 1rem;
--button-font-size--small: 0.9rem;
--button-padding--default: var(--default-padding--sm) var(--default-padding);
--button-padding--small: var(--default-padding--xs) var(--default-padding--sm);
--button-padding: var(--button-padding--default);
--button-shadow: 0 2px 2px #3331;
--notice-color--info: #39d;
--popover-border-color: var(--default-border-color);
--popover-shadow: 0 8px 8px #3333;
}
/* ======== Global Setup ======== */
@font-face {
font-family: "Funnel Sans";
src: url("../funnel_sans/funnel_sans_variable.ttf");
}
@view-transitions {
navigation: auto;
}
/* https://css-tricks.com/inclusively-hidden/ */
.sr-only:not(:focus):not(:active) {
clip: rect(0 0 0 0);
clip-path: inset(50%);
height: 1px;
overflow: hidden;
position: absolute;
white-space: nowrap;
width: 1px;
}
/* ======== Basic Elements / Style Resets ======== */
html {
font-family: var(--default-font-family);
}
button {
appearance: none;
background: none;
border: none;
padding: 0;
box-sizing: border-box;
cursor: pointer;
font-family: inherit;
font-size: inherit;
font-weight: inherit;
}
input[type="text"] {
appearance: none;
background: none;
border: none;
box-sizing: border-box;
font-family: inherit;
font-size: inherit;
font-weight: inherit;
outline: none;
}
a {
color: var(--a-color);
text-decoration: none;
}
/* ======== Utility Classes ======== */
.text--data {
font-family: var(--default-font-family--data);
}
.padded {
padding: var(--default-padding);
}
.padded-sm {
padding: var(--default-padding--sm);
}
.padded--lg {
padding: var(--default-padding--lg);
}
/* ======== Components: Buttons ======== */
.button {
--button-color: inherit;
background: var(--button-background);
border: solid 1px var(--button-border-color);
border-radius: var(--button-border-radius);
box-shadow: var(--button-shadow);
color: var(--button-color);
font-family: var(--button-font-family);
font-size: var(--button-font-size);
padding: var(--button-padding);
text-decoration: none;
transition: background 0.2s ease;
&:hover {
border-color: oklch(from var(--button-border-color) calc(l * 0.95) c h);
background: oklch(from var(--button-background) calc(l * 0.95) c h);
}
}
.button--primary {
--button-background: var(--button-background--primary);
--button-border-color: var(--button-border-color--primary);
--button-color: var(--button-color--primary);
}
.button--secondary {
--button-background: var(--button-background--secondary);
--button-border-color: var(--button-border-color--secondary);
--button-color: var(--button-color--secondary);
}
.button--clear {
--button-background: transparent;
--button-border-color: transparent;
--button-color: inherit;
--button-font-family: inherit;
--button-shadow: none;
}
.button--small {
--button-font-size: var(--button-font-size--small);
--button-padding: var(--button-padding--small);
}
/* ========= Components: Popovers ======== */
.popover:popover-open {
background: #fff;
border: solid 1px var(--popover-border-color);
border-radius: var(--default-border-radius--rounded);
box-shadow: var(--popover-shadow);
display: block;
inset: unset;
margin: 0;
margin-top: 4px;
padding: 0;
position: relative;
}
.dialog:popover-open, .dialog:open {
background: #fff;
border: solid 1px var(--popover-border-color);
border-radius: var(--default-border-radius--rounded);
box-shadow: var(--popover-shadow);
display: block;
max-height: 90vh;
overflow: auto;
}
.basic-dropdown__menu {
list-style-type: none;
margin: 0;
padding: var(--default-padding--sm) 0;
& > li {
padding: 0;
&:hover {
background: #0001;
}
& > button, & > [role="button"] {
background: transparent;
color: inherit;
display: block;
font-family: var(--button-font-family--default);
padding: var(--button-padding--default);
text-decoration: none;
transition: background 0.2s ease;
}
}
}
/* ======== Components: Miscellaneous ======== */
.table {
border-collapse: collapse;
th, td {
border: solid 1px var(--default-border-color);
padding: var(--default-padding--sm);
}
th {
background: #eee;
}
.table__message {
opacity: 0.5;
text-align: center;
}
}
.section {
padding: 1rem 2rem;
}
.notice {
border-radius: var(--default-border-radius--rounded);
margin: 1rem 0rem;
padding: 1rem;
max-width: 40rem;
&.notice--info {
border: solid 1px globals.$notice-color-info;
background: oklch(from var(--notice-color--info) calc(l * 0.9) c h);
color: oklch(from var(--notice-color--info) calc(l * 0.2) c h);
}
}
.permissions-list {
display: flex;
flex-wrap: wrap;
gap: var(--default-padding--sm);
}
.permission-badge {
--permission-badge-resource-color: #f9f9f9;
--permission-badge-description-color: #eee;
display: flex;
flex-grow: 0;
flex-shrink: 0;
}
.permission-badge__resource {
background: var(--permission-badge-resource-color);
border: solid 1px oklch(from var(--permission-badge-resource-color) calc(l * 0.8) c h);
border-right: none;
border-bottom-left-radius: var(--default-border-radius--rounded-sm);
border-top-left-radius: var(--default-border-radius--rounded-sm);
font-family: var(--default-font-family--data);
font-size: 0.9rem;
padding: var(--default-padding--xs);
}
.permission-badge__description {
background: var(--permission-badge-description-color);
border: solid 1px oklch(from var(--permission-badge-description-color) calc(l * 0.8) c h);
border-bottom-right-radius: var(--default-border-radius--rounded-sm);
border-top-right-radius: var(--default-border-radius--rounded-sm);
font-family: var(--default-font-family--data);
font-size: 0.9rem;
padding: var(--default-padding--xs);
}
/* ======== Forms ======== */
.form__label {
display: block;
font-weight: 600;
margin-top: var(--default-padding);
}
.form__input {
display: block;
margin-top: var(--default-padding--sm);
font-family: var(--default-font-family--data);
&:is(input[type="text"]) {
border: solid 1px var(--default-border-color);
border-radius: var(--default-border-radius--rounded);
padding: 0.5rem;
}
}
.form__buttons {
display: flex;
margin-top: var(--default-padding);
&.form__buttons--justify-end {
justify-content: end;
}
button {
margin: 0 var(--default-padding--xs);
&:first-child {
margin-left: 0;
}
&:last-child {
margin-right: 0;
}
}
}
/* ======== Layout ======== */
.page-grid {
height: 100vh;
width: 100vw;
display: grid;
grid-template:
'sidebar toolbar' 4rem
'sidebar main' 1fr / max-content 1fr;
.page-grid__toolbar {
align-items: center;
border-bottom: solid 1px var(--default-border-color);
display: grid;
grid-area: toolbar;
grid-template: 'utilities user' 1fr / 1fr max-content;
.toolbar-item {
flex: 0;
}
}
.page-grid__toolbar-utilities {
align-items: center;
display: flex;
gap: 12px;
grid-area: utilities;
justify-content: flex-start;
padding: 0 12px;
}
.page-grid__toolbar-user {
align-items: center;
display: flex;
gap: 12px;
grid-area: user;
justify-content: flex-end;
padding: 0 12px;
basic-dropdown {
--button-background: var(--button-background--secondary);
--button-border-color: var(--button-border-color--secondary);
--button-color: var(--button-color--secondary);
}
}
.page-grid__sidebar {
grid-area: sidebar;
width: 15rem;
max-height: 100vh;
overflow: auto;
border-right: solid 1px var(--default-border-color);
}
.page-grid__main {
grid-area: main;
overflow: auto;
}
}
/* ======== Workspace Nav ======== */
.workspace-nav {
h1, h2, h3, h4, h5, h6 {
margin: 0;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
h1 {
font-size: 1.5rem;
}
h2 {
font-size: 1.25rem;
}
h3 {
font-size: 1.125rem;
}
h4, h5, h6 {
font-size: 1rem;
}
basic-dropdown {
--button-background: var(--button-background--secondary);
--button-border-color: var(--button-border-color--secondary);
--button-color: var(--button-color--secondary);
--button-font-size: var(--button-font-size--small);
--button-padding: var(--button-padding--small);
}
.workspace-nav__section {
margin-top: var(--default-padding);
}
.workspace-nav__menu {
list-style-type: none;
padding: var(--default-padding--xs) 0;
margin: 0;
}
.workspace-nav__heading {
align-items: center;
display: flex;
font-size: inherit;
justify-content: space-between;
padding: var(--default-padding--sm);
padding-bottom: 0;
}
.workspace-nav__menu-item {
display: block;
margin-left: var(--default-padding--sm);
padding: 0;
}
.workspace-nav__menu-leaf {
align-items: center;
border-radius: var(--default-border-radius--rounded-sm);
display: flex;
justify-content: space-between;
padding: var(--default-padding--xs) var(--default-padding--sm);
.workspace-nav__menu-leaf--current, &:hover {
background: #0001;
}
}
.workspace-nav__menu-link {
flex: 1;
padding: var(--default-padding--sm) 0;
}
}

422
static/portal-table.css Normal file
View file

@ -0,0 +1,422 @@
@import "./expression-editor.css";
/* ======== Toolbar ======== */
.filter-menu {
--button-background: var(--button-background--secondary);
--button-border-color: var(--button-border-color--secondary);
--button-color: var(--button-color--secondary);
}
/* ======== Layout ======== */
.table-viewer__layout {
display: grid;
grid-template:
'table' 1fr
'editor' max-content;
height: 100%;
width: 100%;
}
.table-viewer__table {
display: grid;
grid-area: table;
grid-template:
'headers' max-content
'main' 1fr
'inserter' max-content;
height: 100%;
outline: none;
overflow: auto;
width: 100%;
}
.table-viewer__headers {
align-items: stretch;
display: flex;
grid-area: headers;
/*
Ensure that there will be enough space on the right for popovers to
render without overflowing the container.
*/
padding-right: 480px;
}
/* ======== Table Headers ======== */
.field-header,
.field-adder__header-lookalike {
align-items: center;
background: #0001;
border: solid 1px #ccc;
border-top: none;
border-left: none;
display: flex;
flex: none;
font-family: var(--default-font-family--data);
height: 100%;
justify-content: space-between;
padding: 0.25rem;
text-align: left;
}
.field-header .basic-dropdown__button {
--button-background: transparent;
--button-border-color: transparent;
--button-color: var(--button-color--secondary);
--button-shadow: none;
}
.field-header__label {
padding: 4px;
}
.field-header__type-indicator {
align-items: center;
display: flex;
justify-content: center;
padding: 0;
height: 2rem;
width: 2rem;
svg path {
fill: currentColor;
}
}
.field-header__popover:popover-open {
left: anchor(left);
top: anchor(bottom);
padding: 1rem;
position: absolute;
}
/* ======== Component: Field Adder ======== */
.field-adder {
align-items: stretch;
display: flex;
height: 100%;
}
.field-adder__summary-buttons {
align-items: center;
display: flex;
}
.field-adder__header-lookalike:not(.field-adder__header-lookalike--visible) {
display: none;
}
.field-adder__popover:popover-open {
display: grid;
font-family: var(--default-font-family);
grid-template: "completions configs" 1fr / 1fr 2fr;
left: anchor(left);
position: absolute;
top: anchor(bottom);
width: 480px;
}
.field-adder__completions {
align-items: stretch;
border-right: solid 1px var(--default-border-color);
grid-area: completions;
display: flex;
flex-direction: column;
font-family: var(--default-font-family--data);
justify-content: start;
& > button {
display: block;
padding: 0.5rem;
font-weight: normal;
text-align: left;
width: 100%;
&:hover,
&:focus,
&[aria-selected="true"] {
background: #0000001f;
}
}
}
.field-adder__configs {
grid-area: configs;
padding: var(--default-padding);
}
/* ======== Table Body ======== */
.table-viewer__main {
grid-area: main;
overflow-y: auto;
}
.table-viewer__row {
align-items: stretch;
display: flex;
height: 2.25rem;
}
.table-viewer__cell {
align-items: stretch;
border: solid 1px var(--default-border-color);
border-left: none;
border-top: none;
display: flex;
flex: none;
padding: 0;
&:focus {
outline: none;
}
.table-viewer__cell-container {
align-items: center;
display: flex;
flex: none;
user-select: none;
width: 100%;
&.table-viewer__cell-container--selected {
background: #07f3;
}
&.table-viewer__cell-container--cursor {
outline: 3px solid #37f;
outline-offset: -2px;
}
}
.table-viewer__cell-content {
flex: 1;
font-family: var(--default-font-family--data);
&.table-viewer__cell-content--dropdown {
overflow: hidden;
padding: 0 8px;
}
&.table-viewer__cell-content--numeric {
overflow: hidden;
text-align: right;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 8px;
}
&.table-viewer__cell-content--text {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 8px;
}
&.table-viewer__cell-content--timestamp {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 8px;
}
&.table-viewer__cell-content--uuid {
font-family: var(--default-font-family--mono);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
padding: 0 8px;
}
&.table-viewer__cell-content--null {
align-items: center;
color: #aaa;
display: flex;
font-family: var(--default-font-family--data);
font-style: oblique;
justify-content: center;
padding: 0 4px;
svg path {
fill: currentColor;
}
}
}
}
.table-viewer__notice {
align-items: center;
color: #888;
display: flex;
padding: 0 4px;
svg path {
fill: currentColor;
}
}
.table-viewer__inserter {
grid-area: inserter;
margin-bottom: 2rem;
.table-viewer__inserter-help {
font-size: 1rem;
font-weight: 300;
margin: 8px;
opacity: 0.5;
}
.table-viewer__inserter-main {
align-items: stretch;
display: flex;
justify-items: flex-start;
}
.table-viewer__inserter-rows {
.table-viewer__cell {
border: dashed 1px var(--default-border-color);
border-left: none;
&:last-child {
border-right: none;
}
}
}
.table-viewer__inserter-submit {
align-items: center;
border: dashed 1px var(--button-background--primary);
border-bottom-right-radius: var(--default-border-radius--rounded-sm);
border-top-right-radius: var(--default-border-radius--rounded-sm);
color: var(--button-background--primary);
display: flex;
justify-content: center;
padding: 0 var(--default-padding);
svg path {
fill: currentColor;
}
}
}
/* ======== Datum Editor ======== */
.datum-editor {
border-top: solid 1px var(--default-border-color);
display: flex;
grid-area: editor;
height: 6rem;
.datum-editor__container {
border-left: solid 4px transparent;
display: grid;
flex: 1;
grid-template:
'null-control value-control' max-content
'helpers helpers' auto / max-content auto;
&:has(:focus) {
border-left-color: #07f;
}
&.datum-editor__container--incomplete {
border-left-color: #f33;
}
}
.datum-editor__type-selector {
grid-area: type-selector;
}
.datum-editor__null-control {
align-self: start;
grid-area: null-control;
padding: 12px;
&.datum-editor__null-control--disabled {
opacity: 0.75;
}
}
.datum-editor__text-input {
grid-area: value-control;
font-family: var(--default-font-family--data);
padding: 12px 8px;
}
.datum-editor__timestamp-inputs {
align-items: center;
display: flex;
grid-area: value-control;
justify-content: start;
}
.datum-editor__helpers {
grid-area: helpers;
overflow: auto;
}
.datum-editor__dropdown-options {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 8px;
justify-content: flex-start;
margin: 0;
padding: 0 8px;
}
}
/* ======== Data Display ======== */
.dropdown-option-badge {
--badge-color-base: #f99;
--badge-hue-rot: 0;
--badge-background: oklch(from var(--badge-color-base) l c calc(h + var(--badge-hue-rot)));
background: var(--badge-background);
border-radius: 999px;
color: oklch(from var(--badge-background) calc(l * 0.3) c h);
display: block;
width: max-content;
&:not(:has(button)) {
padding: 6px 12px;
}
&.dropdown-option-badge--red {
--badge-hue-rot: 50;
}
&.dropdown-option-badge--orange {
--badge-hue-rot: 50;
}
&.dropdown-option-badge--yellow {
--badge-hue-rot: 100;
}
&.dropdown-option-badge--green {
--badge-hue-rot: 150;
}
&.dropdown-option-badge--blue {
--badge-hue-rot: 200;
}
&.dropdown-option-badge--indigo {
--badge-hue-rot: 250;
}
&.dropdown-option-badge--violet {
--badge-hue-rot: 300;
}
button {
color: inherit;
padding: 6px 12px;
}
}

View file

@ -3,7 +3,6 @@
// `shadowRoot` field must remain as the default, else named slots break. // `shadowRoot` field must remain as the default, else named slots break.
props: { props: {
button_aria_label: { attribute: "button-aria-label" }, button_aria_label: { attribute: "button-aria-label" },
button_class: { attribute: "button-class" },
alignment: { attribute: "alignment" }, alignment: { attribute: "alignment" },
}, },
tag: "basic-dropdown", tag: "basic-dropdown",
@ -30,16 +29,10 @@ stylesheet, which is merely a copy of the "main" application stylesheet.
type Props = { type Props = {
alignment?: string; alignment?: string;
button_aria_label?: string; button_aria_label?: string;
button_class?: string;
on_toggle?(ev: ToggleEvent): unknown; on_toggle?(ev: ToggleEvent): unknown;
}; };
let { let { alignment, button_aria_label, on_toggle }: Props = $props();
alignment,
button_aria_label,
button_class = "button--secondary",
on_toggle,
}: Props = $props();
let popover_element: HTMLElement | undefined = $state(); let popover_element: HTMLElement | undefined = $state();
// Hacky workaround because as of September 2025 implicit anchor association // Hacky workaround because as of September 2025 implicit anchor association
@ -69,7 +62,7 @@ stylesheet, which is merely a copy of the "main" application stylesheet.
<button <button
aria-label={button_aria_label} aria-label={button_aria_label}
class={["basic-dropdown__button", button_class]} class="basic-dropdown__button"
id="dropdown-button" id="dropdown-button"
onclick={() => { onclick={() => {
popover_element?.showPopover(); popover_element?.showPopover();
@ -91,6 +84,41 @@ stylesheet, which is merely a copy of the "main" application stylesheet.
<slot name="popover"></slot> <slot name="popover"></slot>
</div> </div>
<style lang="scss"> <style lang="css">
@use "../../sass/main"; .basic-dropdown__button {
appearance: none;
box-sizing: border-box;
cursor: pointer;
font-weight: inherit;
background: var(--button-background);
border: solid 1px var(--button-border-color);
border-radius: var(--button-border-radius);
box-shadow: var(--button-shadow);
color: var(--button-color);
font-family: var(--button-font-family);
font-size: var(--button-font-size);
padding: var(--button-padding);
text-decoration: none;
transition: background 0.2s ease;
&:hover {
border-color: oklch(from var(--button-border-color) calc(l * 0.95) c h);
background: oklch(from var(--button-background) calc(l * 0.95) c h);
}
}
.basic-dropdown__popover:popover-open {
background: #fff;
border: solid 1px var(--popover-border-color);
border-radius: var(--default-border-radius--rounded);
box-shadow: var(--popover-shadow);
display: block;
margin: unset;
max-height: 90vh;
overflow: auto;
padding: 0;
position: absolute;
top: anchor(bottom);
}
</style> </style>

View file

@ -1,51 +0,0 @@
<svelte:options
customElement={{
props: { expanded: { type: "Boolean" } },
shadow: "none",
tag: "collapsible-menu",
}}
/>
<!--
@component
An accordion-style collapsible list, designed for vertical navigation.
It exposes two named slots, "summary" (clickable and always visible) and
"content" (visible only when expanded).
-->
<script lang="ts">
type Props = {
expanded?: boolean;
};
// `expanded` is marked as $bindable to make it clear that it takes the place
// of dynamic (that is, uncontrolled) internal state as well as an externally
// controllable and inspectable value. In practice this may be used to set an
// initial value for `expanded` without the need for, e.g., an
// `initial_expanded` prop as well as an internal variable to keep track of
// the actual current state.
let { expanded = $bindable(true) }: Props = $props();
function handle_summary_click() {
expanded = !expanded;
}
</script>
<div class="collapsible-menu">
<button
class="collapsible-menu__sumary"
onclick={handle_summary_click}
type="button"
>
<slot name="summary"></slot>
</button>
<div
class={[
"collapsible-menu__content",
expanded && "collapsible-menu__content--expanded",
]}
>
<slot name="content"></slot>
</div>
</div>

View file

@ -15,21 +15,22 @@
import { type PgExpressionAny } from "./expression.svelte"; import { type PgExpressionAny } from "./expression.svelte";
import ExpressionEditor from "./expression-editor.webc.svelte"; import ExpressionEditor from "./expression-editor.webc.svelte";
import { type FieldInfo } from "./field.svelte"; import { type FieldInfo } from "./field.svelte";
import { type Presentation } from "./presentation.svelte"; import { RFC_3339_S, type Presentation } from "./presentation.svelte";
import type { Datum } from "./datum.svelte"; import type { Datum } from "./datum.svelte";
const ASSIGNABLE_PRESENTATIONS: Presentation[] = [ const ASSIGNABLE_PRESENTATIONS: Presentation[] = [
{ t: "Text", c: { input_mode: { t: "MultiLine", c: {} } } }, { t: "Text", c: { input_mode: { t: "MultiLine", c: {} } } },
{ t: "Timestamp", c: {} }, { t: "Timestamp", c: { format: RFC_3339_S } },
{ t: "Uuid", c: {} }, { t: "Uuid", c: {} },
]; ];
const ASSIGNABLE_FIELDS: FieldInfo[] = ASSIGNABLE_PRESENTATIONS.map( const ASSIGNABLE_FIELDS: FieldInfo[] = ASSIGNABLE_PRESENTATIONS.map(
(presentation) => ({ (presentation) => ({
field: { field: {
id: "", id: "",
table_label: "",
name: "", name: "",
ordinality: -1,
presentation, presentation,
table_label: "",
table_width_px: -1, table_width_px: -1,
}, },
not_null: true, not_null: true,

View file

@ -136,7 +136,7 @@
} }
</script> </script>
<div class="expression-selector__container"> <div class="expression-selector">
<button <button
aria-label={`Select expression type (current: ${iconography_current?.label ?? "None"})`} aria-label={`Select expression type (current: ${iconography_current?.label ?? "None"})`}
bind:this={menu_button_element} bind:this={menu_button_element}
@ -154,7 +154,7 @@
</button> </button>
<div <div
bind:this={popover_element} bind:this={popover_element}
class="expression-selector__popover" class="popover expression-selector__popover"
popover="auto" popover="auto"
style:position-anchor={anchor_name} style:position-anchor={anchor_name}
> >

View file

@ -16,7 +16,6 @@ submission.
incompatible with the current presentation configuration.--> incompatible with the current presentation configuration.-->
<script lang="ts"> <script lang="ts">
import { toLowerCase } from "zod";
import FieldDetails from "./field-details.svelte"; import FieldDetails from "./field-details.svelte";
import { import {
all_presentation_tags, all_presentation_tags,
@ -131,9 +130,12 @@ incompatible with the current presentation configuration.-->
} }
</script> </script>
<div class="container"> <div class="field-adder">
<div <div
class={["header-lookalike", expanded && "visible"]} class={[
"field-adder__header-lookalike",
expanded && "field-adder__header-lookalike--visible",
]}
style:anchor-name={anchor_name} style:anchor-name={anchor_name}
> >
<input <input
@ -156,11 +158,11 @@ incompatible with the current presentation configuration.-->
<form method="post" action="add-field"> <form method="post" action="add-field">
<div <div
bind:this={popover_element} bind:this={popover_element}
class="phono-popover popover" class="popover field-adder__popover"
popover="auto" popover="auto"
style:position-anchor={anchor_name} style:position-anchor={anchor_name}
> >
<div class="completions" role="listbox"> <div class="field-adder__completions" role="listbox">
{#each columns {#each columns
.map(({ name }) => name) .map(({ name }) => name)
.filter((name) => name .filter((name) => name
@ -184,7 +186,7 @@ incompatible with the current presentation configuration.-->
</button> </button>
{/each} {/each}
</div> </div>
<div class="configs"> <div class="field-adder__configs">
{#if presentation_value} {#if presentation_value}
<FieldDetails <FieldDetails
bind:name_value bind:name_value
@ -196,7 +198,9 @@ incompatible with the current presentation configuration.-->
}} }}
/> />
{/if} {/if}
<button class="button--primary" type="submit">Create</button> <div class="form__buttons">
<button class="button button--primary" type="submit">Create</button>
</div>
</div> </div>
</div> </div>
</form> </form>
@ -205,7 +209,7 @@ incompatible with the current presentation configuration.-->
<div class="field-adder__summary-buttons"> <div class="field-adder__summary-buttons">
<button <button
aria-label="toggle field adder" aria-label="toggle field adder"
class="button--clear" class="button button--clear"
onclick={toggle_expanded} onclick={toggle_expanded}
type="button" type="button"
> >
@ -215,83 +219,4 @@ incompatible with the current presentation configuration.-->
</div> </div>
<style lang="css"> <style lang="css">
/*
I've been annoyed by some of the rough edges around global SCSS in web
components, and independently curious about simplifying the build process by
replacing SCSS with modern vanilla CSS entirely, so trying something new here.
TBD whether it gets adopted more widely, or reverted.
*/
@import "../../css_dist/main.css";
.container {
--default-border-color: #ccc;
--viewer-th-background: #0001;
--viewer-th-border: solid 1px #ccc;
--viewer-th-font-family: "Funnel Sans";
--viewer-th-font-weight: bolder;
--viewer-th-padding-x: 8px;
--viewer-th-padding-y: 4px;
align-items: stretch;
display: flex;
height: 100%;
}
.header-lookalike {
align-items: stretch;
background: var(--viewer-th-background);
border: var(--viewer-th-border);
border-top: none;
&:first-child {
border-left: none;
}
display: none;
&.visible {
display: flex;
}
flex-direction: column;
font-family: var(--viewer-th-font-family);
font-weight: var(--viewer-th-font-weight);
height: 100%; /* css hack to make percentage based cell heights work */
justify-content: center;
padding: var(--viewer-th-padding-y) var(--viewer-th-padding-x);
}
.popover:popover-open {
display: grid;
grid-template: "completions configs" 1fr / 1fr 2fr;
left: anchor(left);
position: absolute;
top: anchor(bottom);
width: 480px;
}
.completions {
border-right: solid 1px var(--default-border-color);
grid-area: completions;
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: start;
& > button {
display: block;
padding: 0.5rem;
font-weight: normal;
text-align: left;
width: 100%;
&:hover,
&:focus,
&[aria-selected="true"] {
background: #0000001f;
}
}
}
.configs {
grid-area: configs;
padding: 16px;
}
</style> </style>

View file

@ -100,31 +100,31 @@ field. This is typically rendered within a popover component, and within an HTML
} }
</script> </script>
<h2 class="form-section__heading">Field Details</h2> <h2>Field Details</h2>
<label class="form-section"> <label class="form__section">
<div class="form-section__label">SQL-friendly Name</div> <div class="form__label">SQL-friendly Name</div>
<input <input
bind:value={name_value} bind:value={name_value}
class="form-section__input form-section__input--text" class="form__input"
disabled={name_input_disabled} disabled={name_input_disabled}
name="name" name="name"
type="text" type="text"
/> />
</label> </label>
<label class="form-section"> <label class="form__section">
<div class="form-section__label">Human-friendly Label</div> <div class="form__label">Human-friendly Label</div>
<input <input
bind:value={label_value} bind:value={label_value}
class="form-section__input form-section__input--text" class="form__input"
name="table_label" name="table_label"
oninput={on_name_input} oninput={on_name_input}
type="text" type="text"
/> />
</label> </label>
<label class="form-section"> <label class="form__section">
<div class="form-section__label">Present As</div> <div class="form__label">Present As</div>
<select <select
class="form-section__input" class="form__input"
name="presentation_tag" name="presentation_tag"
onchange={handle_presentation_tag_change} onchange={handle_presentation_tag_change}
value={presentation?.t} value={presentation?.t}
@ -137,7 +137,7 @@ field. This is typically rendered within a popover component, and within an HTML
</select> </select>
</label> </label>
{#if presentation?.t === "Dropdown"} {#if presentation?.t === "Dropdown"}
<div class="form-section"> <div class="form__section">
<ul class="field-details__dropdown-options-list"> <ul class="field-details__dropdown-options-list">
{#each presentation.c.options as option, i} {#each presentation.c.options as option, i}
<li class="field-details__dropdown-option"> <li class="field-details__dropdown-option">
@ -152,12 +152,12 @@ field. This is typically rendered within a popover component, and within an HTML
<input <input
type="text" type="text"
name="dropdown_option_values" name="dropdown_option_values"
class="form-section__input--text" class="form__input--text"
value={option.value} value={option.value}
/> />
<button <button
aria-label="Remove option" aria-label="Remove option"
class="button--clear" class="button button--clear"
onclick={() => { onclick={() => {
if (presentation?.t !== "Dropdown") { if (presentation?.t !== "Dropdown") {
console.warn( console.warn(
@ -177,7 +177,7 @@ field. This is typically rendered within a popover component, and within an HTML
{/each} {/each}
</ul> </ul>
<button <button
class="button--secondary" class="button button--secondary"
onclick={() => { onclick={() => {
if (presentation?.t !== "Dropdown") { if (presentation?.t !== "Dropdown") {
console.warn( console.warn(
@ -196,10 +196,10 @@ field. This is typically rendered within a popover component, and within an HTML
</button> </button>
</div> </div>
{:else if presentation?.t === "Text"} {:else if presentation?.t === "Text"}
<label class="form-section"> <label class="form__section">
<div class="form-section__label">Input Mode</div> <div class="form__label">Input Mode</div>
<select <select
class="form-section__input" class="form__input"
name="text_input_mode" name="text_input_mode"
onchange={handle_text_input_mode_change} onchange={handle_text_input_mode_change}
value={presentation.c.input_mode.t} value={presentation.c.input_mode.t}

View file

@ -38,7 +38,7 @@
<div <div
aria-colindex={index} aria-colindex={index}
class="field-header__container" class="field-header"
draggable={true} draggable={true}
{ondragenter} {ondragenter}
{ondragleave} {ondragleave}
@ -52,11 +52,9 @@
<div class="field-header__label"> <div class="field-header__label">
{field.field.table_label ?? field.field.name} {field.field.table_label ?? field.field.name}
</div> </div>
<div class="field-header__menu-container">
<BasicDropdown <BasicDropdown
alignment="right" alignment="right"
button_aria_label="Field options" button_aria_label="Field options"
button_class="field-header__type-indicator"
on_toggle={(ev) => { on_toggle={(ev) => {
if (ev.newState === "closed") { if (ev.newState === "closed") {
type_indicator_element?.focus(); type_indicator_element?.focus();
@ -101,7 +99,6 @@
</li> </li>
</menu> </menu>
</BasicDropdown> </BasicDropdown>
</div>
</div> </div>
<dialog bind:this={remove_field_dialog_element} class="dialog"> <dialog bind:this={remove_field_dialog_element} class="dialog">
@ -116,16 +113,16 @@
Remove field and underlying data. This cannot be undone. Remove field and underlying data. This cannot be undone.
</label> </label>
</div> </div>
<div class="padded"> <div class="form__buttons">
<input type="hidden" name="field_id" value={field.field.id} /> <input type="hidden" name="field_id" value={field.field.id} />
<button class="button--primary" type="submit">Remove</button> <button class="button button--primary" type="submit">Remove</button>
<button <button
class="button--secondary" class="button button--secondary"
onclick={() => { onclick={() => remove_field_dialog_element?.close()}
remove_field_dialog_element?.close(); type="button"
}}
type="button">Cancel</button
> >
Cancel
</button>
</div> </div>
</form> </form>
</dialog> </dialog>
@ -141,8 +138,8 @@
/> />
</div> </div>
<input type="hidden" name="field_id" value={field.field.id} /> <input type="hidden" name="field_id" value={field.field.id} />
<div class="padded"> <div class="form__buttons">
<button class="button--primary" type="submit">Save</button> <button class="button button--primary" type="submit">Save</button>
</div> </div>
</form> </form>
</dialog> </dialog>

View file

@ -11,6 +11,7 @@
<script lang="ts"> <script lang="ts">
import { type PgExpressionAny } from "./expression.svelte"; import { type PgExpressionAny } from "./expression.svelte";
import BasicDropdown from "./basic-dropdown.webc.svelte";
import ExpressionEditor from "./expression-editor.webc.svelte"; import ExpressionEditor from "./expression-editor.webc.svelte";
type Props = { type Props = {
@ -20,44 +21,33 @@
let { identifier_hints = [], initialValue }: Props = $props(); let { identifier_hints = [], initialValue }: Props = $props();
let popover_element = $state<HTMLDivElement | undefined>();
let expr = $state<PgExpressionAny | undefined>(initialValue ?? undefined); let expr = $state<PgExpressionAny | undefined>(initialValue ?? undefined);
function handle_toolbar_button_click() {
popover_element?.togglePopover();
}
function handle_clear_button_click() { function handle_clear_button_click() {
expr = undefined; expr = undefined;
} }
</script> </script>
<div class="toolbar-item"> <div class="filter-menu toolbar-item">
<button <BasicDropdown>
class="button--secondary" <span slot="button-contents">Filter</span>
onclick={handle_toolbar_button_click} <form action="set-filter" class="padded" method="post" slot="popover">
type="button"
>
Filter
</button>
<div bind:this={popover_element} class="toolbar-popover" popover="auto">
<form action="set-filter" method="post">
<ExpressionEditor bind:value={expr} {identifier_hints} /> <ExpressionEditor bind:value={expr} {identifier_hints} />
<div class="toolbar-popover__form-actions"> <div class="form__buttons">
<input <input
name="filter_expression" name="filter_expression"
type="hidden" type="hidden"
value={JSON.stringify(expr)} value={JSON.stringify(expr)}
/> />
<button <button
class="button--secondary" class="button button--secondary"
onclick={handle_clear_button_click} onclick={handle_clear_button_click}
type="button" type="button"
> >
Clear Clear
</button> </button>
<button class="button--primary" type="submit">Apply</button> <button class="button button--primary" type="submit">Apply</button>
</div> </div>
</form> </form>
</div> </BasicDropdown>
</div> </div>

View file

@ -4,6 +4,8 @@ import { type Datum } from "./datum.svelte.ts";
type Assert<_T extends true> = void; type Assert<_T extends true> = void;
export const RFC_3339_S = "yyyy-MM-dd'T'HH:mm:ssXXX";
export const all_presentation_tags = [ export const all_presentation_tags = [
"Dropdown", "Dropdown",
"Numeric", "Numeric",

View file

@ -62,7 +62,7 @@
aria-rowindex={coords.row_idx} aria-rowindex={coords.row_idx}
aria-selected={selected} aria-selected={selected}
bind:this={cell_element} bind:this={cell_element}
class="lens-cell" class="table-viewer__cell"
{oncopy} {oncopy}
ondblclick={(ev) => ondblclick?.(ev, coords)} ondblclick={(ev) => ondblclick?.(ev, coords)}
onfocus={(ev) => onfocus?.(ev, coords)} onfocus={(ev) => onfocus?.(ev, coords)}
@ -74,15 +74,15 @@
tabindex={selected ? 0 : -1} tabindex={selected ? 0 : -1}
> >
<div <div
class="lens-cell__container" class="table-viewer__cell-container"
class:lens-cell__container--cursor={cursor} class:table-viewer__cell-container--cursor={cursor}
class:lens-cell__container--selected={selected} class:table-viewer__cell-container--selected={selected}
> >
{#if field.field.presentation.t === "Dropdown"} {#if field.field.presentation.t === "Dropdown"}
{#if value.t === "Text"} {#if value.t === "Text"}
<div <div
class="lens-cell__content lens-cell__content--dropdown" class="table-viewer__cell-content table-viewer__cell-content--dropdown"
class:lens-cell__content--null={value.c === undefined} class:table-viewer__cell-content--null={value.c === undefined}
> >
{#if value.c === undefined} {#if value.c === undefined}
<i class={["ti", null_value_class]}></i> <i class={["ti", null_value_class]}></i>
@ -107,8 +107,8 @@
{/if} {/if}
{:else if field.field.presentation.t === "Numeric"} {:else if field.field.presentation.t === "Numeric"}
<div <div
class="lens-cell__content lens-cell__content--numeric" class="table-viewer__cell-content table-viewer__cell-content--numeric"
class:lens-cell__content--null={value.c === undefined} class:table-viewer__cell-content--null={value.c === undefined}
> >
{#if value.c === undefined} {#if value.c === undefined}
<i class={["ti", null_value_class]}></i> <i class={["ti", null_value_class]}></i>
@ -118,8 +118,8 @@
</div> </div>
{:else if field.field.presentation.t === "Text"} {:else if field.field.presentation.t === "Text"}
<div <div
class="lens-cell__content lens-cell__content--text" class="table-viewer__cell-content table-viewer__cell-content--text"
class:lens-cell__content--null={value.c === undefined} class:table-viewer__cell-content--null={value.c === undefined}
> >
{#if value.c === undefined} {#if value.c === undefined}
<i class={["ti", null_value_class]}></i> <i class={["ti", null_value_class]}></i>
@ -129,8 +129,8 @@
</div> </div>
{:else if field.field.presentation.t === "Uuid"} {:else if field.field.presentation.t === "Uuid"}
<div <div
class="lens-cell__content lens-cell__content--uuid" class="table-viewer__cell-content table-viewer__cell-content--uuid"
class:lens-cell__content--null={value.c === undefined} class:table-viewer__cell-content--null={value.c === undefined}
> >
{#if value.c === undefined} {#if value.c === undefined}
<i class={["ti", null_value_class]}></i> <i class={["ti", null_value_class]}></i>
@ -140,8 +140,8 @@
</div> </div>
{:else if field.field.presentation.t === "Timestamp"} {:else if field.field.presentation.t === "Timestamp"}
<div <div
class="lens-cell__content lens-cell__content--timestamp" class="table-viewer__cell-content table-viewer__cell-content--timestamp"
class:lens-cell__content--null={value.c === undefined} class:table-viewer__cell-content--null={value.c === undefined}
> >
{#if value.c === undefined} {#if value.c === undefined}
<i class={["ti", null_value_class]}></i> <i class={["ti", null_value_class]}></i>
@ -150,12 +150,14 @@
{/if} {/if}
</div> </div>
{:else} {:else}
<div class="lens-cell__content lens-cell__content--unknown"> <div
class="table-viewer__cell-content table-viewer__cell-content--unknown"
>
<div>UNKNOWN</div> <div>UNKNOWN</div>
</div> </div>
{/if} {/if}
{#if invalid_value} {#if invalid_value}
<div class="lens-cell__notice"> <div class="table-viewer__cell-notice">
<i class="ti ti-alert-circle"></i> <i class="ti ti-alert-circle"></i>
</div> </div>
{/if} {/if}

View file

@ -572,7 +572,7 @@
})} })}
{#if lazy_data} {#if lazy_data}
{#each rows as row, row_idx} {#each rows as row, row_idx}
<div class="lens-table__row" role="row"> <div class="table-viewer__row" role="row">
{#each lazy_data.fields as field, field_idx} {#each lazy_data.fields as field, field_idx}
<TableCell <TableCell
coords={{ region, row_idx, field_idx }} coords={{ region, row_idx, field_idx }}
@ -604,10 +604,10 @@
{/if} {/if}
{/snippet} {/snippet}
<div class="lens-grid"> <div class="table-viewer__layout">
{#if lazy_data} {#if lazy_data}
<div class="lens-table" role="grid"> <div class="table-viewer__table" role="grid">
<div class="lens-table__headers"> <div class="table-viewer__headers">
{#each lazy_data.fields as _, field_index} {#each lazy_data.fields as _, field_index}
<FieldHeader <FieldHeader
bind:field={lazy_data.fields[field_index]} bind:field={lazy_data.fields[field_index]}
@ -630,11 +630,11 @@
}} }}
/> />
{/each} {/each}
<div class="lens-table__header-actions"> <div class="table-viewer__header-actions">
<FieldAdder {columns}></FieldAdder> <FieldAdder {columns}></FieldAdder>
</div> </div>
</div> </div>
<div class="lens-table__main"> <div class="table-viewer__main">
{@render table_region({ {@render table_region({
region: "main", region: "main",
rows: lazy_data.rows, rows: lazy_data.rows,
@ -655,12 +655,12 @@
})} })}
</div> </div>
<form method="post" action="insert"> <form method="post" action="insert">
<div class="lens-inserter"> <div class="table-viewer__inserter">
<h3 class="lens-inserter__help"> <h3 class="table-viewer__inserter-help">
Insert rows &mdash; press "shift + enter" to jump here or add a row Insert rows &mdash; press "shift + enter" to jump here or add a row
</h3> </h3>
<div class="lens-inserter__main"> <div class="table-viewer__inserter-main">
<div class="lens-inserter__rows"> <div class="table-viewer__inserter-rows">
{@render table_region({ {@render table_region({
region: "inserter", region: "inserter",
rows: inserter_rows, rows: inserter_rows,
@ -682,7 +682,7 @@
</div> </div>
<button <button
aria-label="Insert rows" aria-label="Insert rows"
class="lens-inserter__submit" class="table-viewer__inserter-submit"
onkeydown={(ev) => { onkeydown={(ev) => {
// Prevent keypress (e.g. pressing Enter on the button to submit // Prevent keypress (e.g. pressing Enter on the button to submit
// it) from triggering a table interaction. // it) from triggering a table interaction.
@ -706,7 +706,7 @@
{/each} {/each}
</form> </form>
</div> </div>
<div class="table-viewer__datum-editor"> <div class="datum-editor">
{#if selections.length !== 0 && selections.every(({ coords: { field_idx } }) => field_idx === selections[0]?.coords.field_idx)} {#if selections.length !== 0 && selections.every(({ coords: { field_idx } }) => field_idx === selections[0]?.coords.field_idx)}
<DatumEditor <DatumEditor
bind:this={datum_editor} bind:this={datum_editor}