feat(ipub,soundtrck): on screen threshold trigger to start playing
This commit is contained in:
@@ -132,6 +132,7 @@ class IPUBBody extends IPUBElement {
|
||||
IPUBImage,
|
||||
IPUBInteraction,
|
||||
IPUBSoundtrack,
|
||||
IPUBTrigger,
|
||||
]) {
|
||||
console.info(`IPUBBody: Defining custom element <${e.elementName}>`);
|
||||
globalThis.customElements.define(e.elementName, e);
|
||||
@@ -282,6 +283,7 @@ class IPUBAudio extends IPUBElement {
|
||||
}
|
||||
|
||||
if (this.#isFading) {
|
||||
// TODO: Be able to force fading to be canceled
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -553,7 +555,7 @@ class IPUBSoundtrack extends IPUBElement {
|
||||
`IPUBSoundtrack: error while trying to play audio, error: ${e}`,
|
||||
{
|
||||
error: e,
|
||||
audio: audio,
|
||||
audio: last,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -563,23 +565,32 @@ class IPUBSoundtrack extends IPUBElement {
|
||||
* @private
|
||||
*/
|
||||
static #observer = (() => {
|
||||
return new IntersectionObserver((entries) => {
|
||||
for (const { intersectionRatio, target, time } of entries) {
|
||||
/** @type {IPUBSoundtrack} */
|
||||
const soundtrack = target;
|
||||
return new IntersectionObserver(
|
||||
(entries) => {
|
||||
for (const { intersectionRatio, target, time } of entries) {
|
||||
/** @type {IPUBSoundtrack} */
|
||||
const soundtrack =
|
||||
target.tagName === IPUBTrigger.elementName
|
||||
? getAncestor(target, IPUBSoundtrack.elementName)
|
||||
: target;
|
||||
|
||||
if (intersectionRatio > 0) {
|
||||
console.debug(`${soundtrack.id} is on screen at ${time}`, soundtrack);
|
||||
this.#onScreenStack.add(soundtrack);
|
||||
} else {
|
||||
console.debug(
|
||||
`${soundtrack.id} is not on screen ${time}`,
|
||||
soundtrack,
|
||||
);
|
||||
this.#onScreenStack.delete(soundtrack);
|
||||
if (intersectionRatio === 1) {
|
||||
console.debug(
|
||||
`${soundtrack.id} is on screen at ${time}`,
|
||||
soundtrack,
|
||||
);
|
||||
this.#onScreenStack.add(soundtrack);
|
||||
} else {
|
||||
console.debug(
|
||||
`${soundtrack.id} is not on screen ${time}`,
|
||||
soundtrack,
|
||||
);
|
||||
this.#onScreenStack.delete(soundtrack);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
{ threshold: 1 },
|
||||
);
|
||||
})();
|
||||
|
||||
/**
|
||||
@@ -637,10 +648,142 @@ class IPUBSoundtrack extends IPUBElement {
|
||||
return;
|
||||
}
|
||||
|
||||
IPUBSoundtrack.#observer.observe(this);
|
||||
const trigger = this.querySelector(IPUBTrigger.elementName);
|
||||
if (trigger) {
|
||||
IPUBSoundtrack.#observer.observe(trigger);
|
||||
} else {
|
||||
IPUBSoundtrack.#observer.observe(this);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(guz013): Handle if element is moved, it's group should be updated
|
||||
// TODO(guz013): Handle if element is deleted/disconnected, it should be removed from observer
|
||||
}
|
||||
|
||||
class IPUBTrigger extends IPUBElement {
|
||||
static elementName = "ipub-trigger";
|
||||
static observedAttributes = ["height", "width"].concat(
|
||||
IPUBElement.observedAttributes,
|
||||
);
|
||||
|
||||
// TODO: Make this observer global
|
||||
/** @private */
|
||||
static #resizeObserver = new ResizeObserver((bodies) => {
|
||||
for (const { target: body, contentRect } of bodies) {
|
||||
const height = Math.max(body.scrollHeight, contentRect.height);
|
||||
const width = Math.max(body.scrollWidth, contentRect.width);
|
||||
|
||||
for (const trigger of IPUBTrigger.#resizableTriggers.get(body)) {
|
||||
const percH = trigger.getAttribute("height");
|
||||
if (percH) {
|
||||
trigger.style.setProperty(
|
||||
"--ipub-height",
|
||||
`${Math.round((height / 100) * Number.parseFloat(percH))}px`,
|
||||
);
|
||||
}
|
||||
|
||||
const percW = trigger.getAttribute("width");
|
||||
if (percW) {
|
||||
trigger.style.setProperty(
|
||||
"--ipub-width",
|
||||
`${Math.round((width / 100) * Number.parseFloat(percW))}px`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
/**
|
||||
* @private
|
||||
* @type {Map<IPUBBody, Set<IPUBTrigger>>}
|
||||
*/
|
||||
static #resizableTriggers = new Map();
|
||||
|
||||
// FIXME: trigger can be the same size as viewport, cap it to 80% of viewport
|
||||
// height and 100% of viewport width
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
const body = getAncestor(this, "ipub-body");
|
||||
if (!body) {
|
||||
console.error("IPUBTrigger: element must be a descendant of ipub-body");
|
||||
return;
|
||||
}
|
||||
|
||||
console.debug(
|
||||
`IPUBTrigger#${this.id}: adding ${IPUBBody.elementName}#${body.id} from resize observer`,
|
||||
);
|
||||
IPUBTrigger.#resizeObserver.observe(body);
|
||||
if (this.getAttribute("height") || this.getAttribute("width")) {
|
||||
IPUBTrigger.#resizableTriggers.set(
|
||||
body,
|
||||
(IPUBTrigger.#resizableTriggers.get(body) || new Set()).add(this),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
super.attributeChangedCallback(name, oldValue, newValue);
|
||||
|
||||
const body = getAncestor(this, "ipub-body");
|
||||
if (!body) {
|
||||
console.error("IPUBTrigger: element must be a descendant of ipub-body");
|
||||
return;
|
||||
}
|
||||
|
||||
const set = IPUBTrigger.#resizableTriggers.get(body) || new Set();
|
||||
if (this.getAttribute("height") || this.getAttribute("width")) {
|
||||
set.add(this);
|
||||
} else {
|
||||
set.delete(this);
|
||||
}
|
||||
|
||||
if (name === "width" || name === "height") {
|
||||
const height = Math.max(
|
||||
body.scrollHeight,
|
||||
body.getBoundingClientRect().height,
|
||||
);
|
||||
const width = Math.max(
|
||||
body.scrollWidth,
|
||||
body.getBoundingClientRect().width,
|
||||
);
|
||||
|
||||
const percH = this.getAttribute("height");
|
||||
if (percH) {
|
||||
this.style.setProperty(
|
||||
"--ipub-height",
|
||||
`${Math.round((height / 100) * Number.parseFloat(percH))}px`,
|
||||
);
|
||||
}
|
||||
|
||||
const percW = this.getAttribute("width");
|
||||
if (percW) {
|
||||
this.style.setProperty(
|
||||
"--ipub-width",
|
||||
`${Math.round((width / 100) * Number.parseFloat(percW))}px`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
const set = IPUBTrigger.#resizableTriggers.get(body) || new Set();
|
||||
set.delete(this);
|
||||
|
||||
if (set.size === 0) {
|
||||
const body = getAncestor(this, "ipub-body");
|
||||
if (!body) {
|
||||
console.error("IPUBTrigger: element must be a descendant of ipub-body");
|
||||
return;
|
||||
}
|
||||
|
||||
console.debug(
|
||||
`IPUBTrigger#${this.id}: removing ${IPUBBody.elementName}#${body.id} from resize observer`,
|
||||
);
|
||||
IPUBTrigger.#resizableTriggers.delete(body);
|
||||
IPUBTrigger.#resizeObserver.unobserve(body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<img src="../images/background0001.jpg" width="100" height="100" />
|
||||
</ipub-background>
|
||||
<ipub-soundtrack style="--ipub-color:cyan">
|
||||
<ipub-trigger height="10" />
|
||||
<!-- TODO: Search on how to make this more accessible, more semantic as using <details> -->
|
||||
<figure>
|
||||
<label>
|
||||
@@ -57,6 +58,7 @@
|
||||
<img src="../images/image0002.png" />
|
||||
</ipub-image>
|
||||
<ipub-soundtrack style="--ipub-color:green;">
|
||||
<ipub-trigger height="10" />
|
||||
<figure>
|
||||
<label>
|
||||
<input type="checkbox" />
|
||||
@@ -90,6 +92,7 @@
|
||||
<img src="../images/image0002.png" />
|
||||
</ipub-image>
|
||||
<ipub-soundtrack style="--ipub-color:yellow;">
|
||||
<ipub-trigger height="10" />
|
||||
<figure>
|
||||
<label>
|
||||
<input type="checkbox" />
|
||||
|
||||
@@ -199,6 +199,18 @@ ipub-soundtrack {
|
||||
&[playing] figure figcaption::before {
|
||||
content: "P "; /* TODO: change to an icon and better positioning */
|
||||
}
|
||||
|
||||
& > ipub-trigger {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transform: translateY(--ipub-offset, 0%);
|
||||
pointer-events: none;
|
||||
|
||||
width: var(--ipub-width, 100%);
|
||||
height: var(--ipub-height, 0%);
|
||||
}
|
||||
}
|
||||
|
||||
ipub-background {
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,5 +3,4 @@ out.css
|
||||
.tmp
|
||||
.env
|
||||
*.db
|
||||
*.epub
|
||||
tmp
|
||||
|
||||
1
.scrollable-audio
Submodule
1
.scrollable-audio
Submodule
Submodule .scrollable-audio added at a0fe0ac5c8
49
go.work.sum
49
go.work.sum
@@ -1,47 +1,16 @@
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.2 h1:t/gZFyrijKuSU0elA5kRngP/oU3mc0I+Dvp8HwRE4c0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.6.2/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.78.1 h1:1M0gSbyP6q06gl3384wpoKPaH9G16NPqZFieEhLboSU=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.78.1/go.mod h1:4qzsZSzB/KiX2EzDjs9D7A8rI/WGJxZceVJIHqtJjIU=
|
||||
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
|
||||
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06 h1:JLvn7D+wXjH9g4Jsjo+VqmzTUpl/LX7vfr6VOfSWTdM=
|
||||
github.com/libsql/sqlite-antlr4-parser v0.0.0-20240327125255-dbf53b6cbf06/go.mod h1:FUkZ5OHjlGPjnM2UyGJz9TypXQFgYqw6AFNO1UiROTM=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/tursodatabase/go-libsql v0.0.0-20241221181756-6121e81fbf92 h1:IYI1S1xt4WdQHjgVYzMa+Owot82BqlZfQV05BLnTcTA=
|
||||
github.com/tursodatabase/go-libsql v0.0.0-20241221181756-6121e81fbf92/go.mod h1:TjsB2miB8RW2Sse8sdxzVTdeGlx74GloD5zJYUC38d8=
|
||||
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
|
||||
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
|
||||
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU=
|
||||
golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
||||
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
|
||||
17
repository/role.go
Normal file
17
repository/role.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"log/slog"
|
||||
|
||||
"code.capytal.cc/loreddev/x/tinyssert"
|
||||
)
|
||||
|
||||
type RoleRepository struct {
|
||||
db *sql.DB
|
||||
|
||||
ctx context.Context
|
||||
log *slog.Logger
|
||||
assert tinyssert.Assertions
|
||||
}
|
||||
Submodule smalltrip updated: 3d201d2122...bb634f58cb
1
templates/projects.html
Normal file
1
templates/projects.html
Normal file
@@ -0,0 +1 @@
|
||||
{{define "projects"}} {{end}}
|
||||
Reference in New Issue
Block a user