Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic Mechanics #20

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open

Basic Mechanics #20

wants to merge 14 commits into from

Conversation

vvvyyynet
Copy link
Contributor

@vvvyyynet vvvyyynet commented Feb 20, 2025

For this PR I refactored the single-file test-route from Tuesday aiming to simplify reuse of the floatingApparatus in other projects (open for other names). Below are some comments

Comments and Questions

  • Where to place clickListeners: Currently I use inconsistent practice: handleNoteClick is attached to every single note whereas handleMarkClick is attached to the mainText-column relying on event-bubbling. What's your opinion on this? It also makes sense.
  • svelte-ignore a11y: I used svelte-ignore for a11y things a few times to avoid adding aria-types on non-buttons... I think it is ok, since the click-actions are purely visual. But I'd like to have a second opinion on this.
  • Placing the trigger of Tailwind-Popover: I would like to use a Tailwind Popover for the <MultiMarkPopup>, but I don't understand how to place its trigger...
  • Getting rid of global styles: When I remove :global() in the css-styles it does not see the DOM-elements inside the {@html}. Another option would be to select for e.g. #container_text, but how to do this using the ids in constants.js? Or would you prefer doing the conditional formatting with .highlighted-classes in pure tailwind?
  • Loading data on localhost: I tried to externalise the json-fetches (like we did in Parzival with metadata). However, since I use local .json files and no server-API I get the following error: "Cannot use relative URL (/json/aco-text.json) with global fetch — use event.fetch instead: https://svelte.dev/docs/kit/web-standards#fetch-apis"

@pdaengeli
Copy link
Contributor

Not a qualified reviewer for this kind of PR, but I assume it is about the comments in a grid column (in ACO displayed on the right of the text), isn't it?

floatingCommentary or floatingAnnotations might be a bit more generic in that case than floatingApparatus (for ACO floatingApparatus is not a bad choice, though).

@flicksolutions
Copy link
Member

flicksolutions commented Feb 21, 2025

  1. Where to place clickListeners:

*Currently I use inconsistent practice: handleNoteClick is attached to every single note whereas handleMarkClick is attached to the mainText-column relying on event-bubbling. What's your opinion on this? It also makes sense.

From the fact, that svelte started yelling at you, you can gather, that the handleMarkClick-way is the wrong way to go ;-)
Attaching a click handler as narrowly as possible makes more sense, in my opinion. And then attach a "no mark clicked"-Handler to the body itself.

  1. svelte-ignore a11y:

*I used svelte-ignore for a11y things a few times to avoid adding aria-types on non-buttons... I think it is ok, since the click-actions are purely visual. But I'd like to have a second opinion on this.

I have to side with svelte on this: the notes at least are clearly buttons, not just a div! A click on them changes the content of the site. One might even say that the spans are actually buttons, or at least anchors, because stuff happens when you click on them.

  1. Placing the trigger of Tailwind-Popover:

*I would like to use a Tailwind Popover for the <MultiMarkPopup>, but I don't understand how to place its trigger...

Why not use Floating UI svelte? https://next.skeleton.dev/docs/integrations/popover/svelte/

  1. Getting rid of global styles:

*When I remove :global() in the css-styles it does not see the DOM-elements inside the {@html}. Another option would be to select for e.g. #container_text, but how to do this using the ids in constants.js? Or would you prefer doing the conditional formatting with .highlighted-classes in pure tailwind?

I dislike the current use of a mix of CSS and tailwind. You will have to set some styles in the CSS-Section of the svelte files, but this doesn't mean you have to stop using tailwind. Use the @apply-keyword in the CSS section to still apply tailwind.

you can narrow down the global styles a lot if you select an element that is accessible in the code and then apply the :global()-pseudo-selector afterwards. e.g.:
.notebox :global(a) {
instead of
:global(.notebox a) {

and never use purely global styles in the style-section of a svelte file. like :global(p) {. if you have global styles, write them into a external css (postcss) file.

  1. Loading data on localhost:

*I tried to externalise the json-fetches (like we did in Parzival with metadata). However, since I use local .json files and no server-API I get the following error: "Cannot use relative URL (/json/aco-text.json) with global fetch — use event.fetch instead: https://svelte.dev/docs/kit/web-standards#fetch-apis"

This sent me a bit back into my rabbithole of where and when to fetch... But the answer might be simpler in this case: since this is local data, we can just import instead of fetch?! Why are those jsons in the static folder instead of in $lib/data directly? I think this is overcomplicating things... butting the jsons into the data-folder directly and just importing them in the load function might be by far the best solution for this...

Copy link
Member

@flicksolutions flicksolutions left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

most importantly: your app doesn't work on dark-mode at the moment. you can force the light mode if you only want one mode (https://next.skeleton.dev/docs/guides/mode). but at the moment in dark mode your text basically vanishes.

another very general thing is, that I think you don't use the full potential of svelte when manipulating the DOM:
In most of your functions you are tediously selecting elements from the document. If you had a binding for your element, you could just pass it to the function.
you could build yourself an object with the id's of the notes as keys and the dom-nodes themselfes as values. With a simple bind:this={notes[id]} you would have a lasting reference to the DOM-node.
Of course it would be a little bit more complicated because you created a component for the note, but still very much doable. this would get rid of most of the document.querySelector-calls in your functions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you explain why this file is necessary? I think it's what breaks my intellisense:
grafik

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea of the index is as follows:
(1) It allows me to import the functions as if the floatingApparatus were an external library, i.e. all functions directly from the parent folder, without explicitly naming the individual files.
(2) I like to visually group similar functions together and make some general comments on their usage, independent of the alphabetic order of their names. It's a bit like combining the benefits of single-file functions with grouping similar stuff.

However I understand, if it seems a bit overkill here... would you like to delete the index?

Btw. I found another huge advantage of single-file functions: they have independent file history!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my VSCodium I don't see the second line handleNoteClick: undefined. No clue where this comes from, especially, since the first box seems to know that the function returns void. So your intellisense does see the function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as discussed, this is not necessary. just move the files here and then import them in the load-function

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. However, I left the imports in content.js which I plan to use as a central file for file imports. I find this easier than to update multiple load-functions if I want to switch APIs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I did move the .json files to the lib/data folder, which indeed is nice for import. However, I wanted to ask you how you differenciate between the static folder and a lib/data folder. I mean, the data is static, too... Do you only use it for media but not for structured data?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, this is nice! I'll implement this in March, when I'll polish the design.

`notebox word-wrap absolute max-w-[700px] rounded-md border-4 bg-white p-3 transition-transform duration-500`,
selectedNote.id === note.id && 'highlighted'
]}
style={`margin-top:${MARGIN_NOTEBOX}px; margin-bottom:${MARGIN_NOTEBOX}px`}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you set this as a style and don't use tailwind?
is for some reason my-2 is not good enough and you need exactly 10 px and you reuse the 10px value often you can set custom styles

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tailwind-classnames composed out of fragments won't render due to treeshaking.

[Npx] is just a personal habit, since I always forget which numbers are valid custom styles and which are not. There's no meaning. Note that I'll do styling in March, so here it just has to work.


<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

svelte is right here. this is a button and not just a div

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also want to get rid of the ignore statements. However, the notes cannot be buttons, since buttons are not permitted to have interactive content. Already now, there are anchor-tags inside the notes for the references. Also think of a nested button that (un)collapses a very long note.

I see two workarounds:

  1. adding aria-roles etc. to the parent div
  2. adding a full-sized invisible button as a sibling of the note-content.

I chose (1) . As I was trying (2) I ran into difficulties related to event-propagation and sizing of the box. It should work in theory, but it isn't optimal either.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

using regex to match tags in the content also feels inherently wrong. It might be smarter to create a custom dom and then use the standard way to traverse a DOM-tree. You can use a multitude of ways for that. I also remember there being a npm-package for using it in node-environments. might be a worth a look

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(On a sidenote: I actually did use insertAdjacentHTML() in an older version to temporarily insert <span>s that wrap text of selected notes. But then I realised, that I can just add classes to the existing spans.)

Ok, so you suggest to use DOM-manipulation in the conversion form Proto-HTML --> Text/LineNums/PageNums. Now, I see why this is semantically more straight forward. But from a performance point of view, do you think it is wise to create the full DOM tree three times, just to dump half of the nodes right away? My gut feeling says that regexp is less work for the client.

Note, that in the ProtoHTML everything is still contained and then with regexp I'm basically filtering out the parts that are needed for the respective columns, while keeping the <br />-tags in all columns, which will guarantee alignment of the lines.

Of course we could pre-process the protoHTML for all three columns, and everything would be fine :-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm... it would need texting as to what's more performant... both options are quite expensive so it might be worth a test.

@vvvyyynet vvvyyynet changed the title Groundwork Basic Mechanics Feb 23, 2025
Copy link
Contributor Author

@vvvyyynet vvvyyynet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your detailed review @flicksolutions. I solved the easy tasks and asked some questions, as long as things are still fresh. Some bigger refactoring tasks I leave for March.

1/2 -- Clicklisteners/a11y: I started a discussion further below.
3 -- Popovers: I'll implement Floating UI Svelte in a later PR. Thanks.
4 -- Global CSS-Styles: I dislike the current tailwind/css mix as well and will use as much tailwind as possible inMarch.
5 -- Loading: I'm now using import instead of fetch.


<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also want to get rid of the ignore statements. However, the notes cannot be buttons, since buttons are not permitted to have interactive content. Already now, there are anchor-tags inside the notes for the references. Also think of a nested button that (un)collapses a very long note.

I see two workarounds:

  1. adding aria-roles etc. to the parent div
  2. adding a full-sized invisible button as a sibling of the note-content.

I chose (1) . As I was trying (2) I ran into difficulties related to event-propagation and sizing of the box. It should work in theory, but it isn't optimal either.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. However, I left the imports in content.js which I plan to use as a central file for file imports. I find this easier than to update multiple load-functions if I want to switch APIs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, I did move the .json files to the lib/data folder, which indeed is nice for import. However, I wanted to ask you how you differenciate between the static folder and a lib/data folder. I mean, the data is static, too... Do you only use it for media but not for structured data?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, this is nice! I'll implement this in March, when I'll polish the design.

if (startId) {
// Handle <note_start> tag
openIds.push(startId);
result += `<note_start note_id='${startId}'></note_start>`;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for being picky on this. I changed it.
Small follow-up: What are your thoughts about <span type="start"> vs. <span class="note-start">?

`notebox word-wrap absolute max-w-[700px] rounded-md border-4 bg-white p-3 transition-transform duration-500`,
selectedNote.id === note.id && 'highlighted'
]}
style={`margin-top:${MARGIN_NOTEBOX}px; margin-bottom:${MARGIN_NOTEBOX}px`}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tailwind-classnames composed out of fragments won't render due to treeshaking.

[Npx] is just a personal habit, since I always forget which numbers are valid custom styles and which are not. There's no meaning. Note that I'll do styling in March, so here it just has to work.

let idsAttribute = JSON.stringify(openIds);
if (openIds.length > 0) {
let multiIdsClass = openIds.length > 1 ? 'multiple-ids' : '';
result += `<span class='mark ${multiIdsClass}' note_ids=${idsAttribute}>${precedingText}</span>`;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@flicksolutions How would you insert an onclick-handler here the svelte way?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add an action with use on the div wrapping the maintext in Text.svelte on line 6:

const addSpanHandlers: Action = (node) => {
		// the node has been mounted in the DOM

		$effect(() => {
			// setup goes here
                       node.querySelector(`span`).forEach(n => n.addEventListener("click",eventhandler))

			return () => {
				// teardown goes here
                               node.querySelector(`span`).forEach(n => n.removeEventListener)
			};
		});
	};

something along those lines

@vvvyyynet
Copy link
Contributor Author

Oh yes, importantly, in this PR I did not focus much on styling, which is why I renamed it from "Groundwork" to "Basic Mechanics". Styling, dark mode etc. will be a seperate task for March.

@vvvyyynet
Copy link
Contributor Author

Also, I still have to type everything... but since this would require renaming all the files from .js to .ts, I think it's better to do this not during a PR?!

@flicksolutions
Copy link
Member

Also, I still have to type everything... but since this would require renaming all the files from .js to .ts, I think it's better to do this not during a PR?!

jup, that's fine. Tell me, if you found a simple way to do this. I have a lot of projects I would like to convert to TS but I'm afraid of very tedious work...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants