@studiotitta/studio-editori 0.1.12 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,9 +11,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
11
11
  Tyylit annetaan `adoptedStyleSheets`-rajapinnalla eikä <style>-elementillä,
12
12
  jotta sivuston tiukka Content-Security-Policy riittää sellaisenaan.
13
13
  ========================================================================== */
14
- import { useEffect, useState } from "react";
14
+ import { usePathname } from "next/navigation";
15
+ import { useEffect, useRef, useState } from "react";
15
16
  import { createPortal } from "react-dom";
16
17
  import { Muokkaustila, PALKIN_TYYLIT } from "./Muokkaustila.js";
18
+ import { taysiLataus } from "./sivunvaihto.js";
17
19
  export function EditorinPalkki() {
18
20
  const [juuri, setJuuri] = useState(null);
19
21
  useEffect(() => {
@@ -29,6 +31,42 @@ export function EditorinPalkki() {
29
31
  setJuuri(varjo);
30
32
  return () => isanta.remove();
31
33
  }, []);
34
+ /*
35
+ Sisäiset linkit täytenä latauksena, ks. sivunvaihto.ts. Kuuntelija on
36
+ ikkunassa ja kaappausvaiheessa, jotta se ehtii ennen Nextin linkkiä.
37
+ */
38
+ useEffect(() => {
39
+ const klikattu = (tapahtuma) => {
40
+ const kohde = tapahtuma.target;
41
+ if (!(kohde instanceof Element))
42
+ return;
43
+ /* Palkin omat painikkeet ovat varjossa, eivät sivun linkkejä. */
44
+ if (kohde.closest("[data-editori-palkki]"))
45
+ return;
46
+ const linkki = kohde.closest("a[href]");
47
+ if (!(linkki instanceof HTMLAnchorElement))
48
+ return;
49
+ const osoite = taysiLataus(tapahtuma, linkki, location.href);
50
+ if (!osoite)
51
+ return;
52
+ tapahtuma.preventDefault();
53
+ tapahtuma.stopPropagation();
54
+ location.assign(osoite);
55
+ };
56
+ window.addEventListener("click", klikattu, true);
57
+ return () => window.removeEventListener("click", klikattu, true);
58
+ }, []);
59
+ /*
60
+ Varmistus muille siirtymille, joita klikkaus ei kata: selaimen
61
+ takaisin-painike ja sivuston omat router.push-kutsut. Jos osoite
62
+ vaihtuu ilman latausta, sivu ladataan uudelleen uudessa osoitteessa.
63
+ */
64
+ const polku = usePathname();
65
+ const alkuperainenPolku = useRef(polku);
66
+ useEffect(() => {
67
+ if (polku !== alkuperainenPolku.current)
68
+ location.reload();
69
+ }, [polku]);
32
70
  if (!juuri)
33
71
  return null;
34
72
  return createPortal(_jsx(Muokkaustila, {}), juuri);
@@ -247,7 +247,7 @@ function enterinOhje(maaritys) {
247
247
  const merkki = maaritys.erotin.trim();
248
248
  return `${merkki === "," ? "Pilkku" : `"${merkki}"`}: uusi kohta`;
249
249
  }
250
- return maaritys.monirivinen ? "Enter: uusi kappale" : "Enter: uusi kohta";
250
+ return maaritys.monirivinen ? "Enter: rivin vaihto" : "Enter: uusi kohta";
251
251
  }
252
252
  /** Sivulla olevat muokattavat kohdat merkintöineen. */
253
253
  function merkinnat() {
@@ -0,0 +1,20 @@
1
+ /** Osa klikkauksesta, jolla päätös tehdään. Testattavissa ilman selainta. */
2
+ export interface Klikkaus {
3
+ button: number;
4
+ metaKey: boolean;
5
+ ctrlKey: boolean;
6
+ shiftKey: boolean;
7
+ altKey: boolean;
8
+ defaultPrevented: boolean;
9
+ }
10
+ /** Osa linkistä, jolla päätös tehdään. */
11
+ export interface Linkki {
12
+ href: string;
13
+ target: string;
14
+ hasAttribute(nimi: string): boolean;
15
+ }
16
+ /**
17
+ * Palauttaa osoitteen, johon siirrytään täydellä latauksella, tai null,
18
+ * jos klikkaus jätetään selaimen ja sivuston hoidettavaksi.
19
+ */
20
+ export declare function taysiLataus(klikkaus: Klikkaus, linkki: Linkki, nykyinen: string): string | null;
@@ -0,0 +1,45 @@
1
+ /* ==========================================================================
2
+ SIVUNVAIHTO MUOKKAAJALLE: AINA KOKONAAN UUSI SIVU
3
+ ==========================================================================
4
+ Muokkaustila lukee sivun muokattavat kohdat ja niiden alkutilan, kun sivu
5
+ ladataan. Nextin linkki ei kuitenkaan lataa sivua: se vaihtaa vain
6
+ sisällön, ja palkki jää muistamaan edellisen sivun. Silloin uuden sivun
7
+ tekstit eivät ole muokattavissa, paneeli näyttää väärän sivun ja
8
+ tallennus voisi verrata tekstejä väärään alkutilaan.
9
+
10
+ Siksi kirjautuneen muokkaajan sisäiset linkit avataan täytenä
11
+ sivulatauksena. Tavallinen kävijä ei saa tätä koodia lainkaan, joten
12
+ sivuston nopeat siirtymät säilyvät hänelle ennallaan. Tallentamattomista
13
+ muutoksista varoittaa selaimen oma beforeunload-kysymys.
14
+ ========================================================================== */
15
+ /**
16
+ * Palauttaa osoitteen, johon siirrytään täydellä latauksella, tai null,
17
+ * jos klikkaus jätetään selaimen ja sivuston hoidettavaksi.
18
+ */
19
+ export function taysiLataus(klikkaus, linkki, nykyinen) {
20
+ /* Uusi välilehti, lataus ja muut erikoisklikkaukset toimivat kuten ennen. */
21
+ if (klikkaus.defaultPrevented || klikkaus.button !== 0)
22
+ return null;
23
+ if (klikkaus.metaKey || klikkaus.ctrlKey || klikkaus.shiftKey || klikkaus.altKey)
24
+ return null;
25
+ if (linkki.hasAttribute("download"))
26
+ return null;
27
+ if (linkki.target && linkki.target !== "_self")
28
+ return null;
29
+ let kohde;
30
+ let tama;
31
+ try {
32
+ tama = new URL(nykyinen);
33
+ kohde = new URL(linkki.href, tama);
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ /* Ulkoiset linkit ja mailto:/tel: eivät ole sivuston sivuja. */
39
+ if (kohde.origin !== tama.origin)
40
+ return null;
41
+ /* Ankkuri samalla sivulla vain vierittää, sivu ei vaihdu. */
42
+ if (kohde.pathname === tama.pathname && kohde.search === tama.search && kohde.hash)
43
+ return null;
44
+ return kohde.href;
45
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@studiotitta/studio-editori",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "Studio Titan sisältöeditori asiakassivustoille: tekstit ja kuvat muokataan suoraan sivulla",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",