seanlowe / obsidian-timelines

Create a timeline view of all notes with the specified combination of tags
https://seanlowe.github.io/obsidian-timelines/
MIT License
54 stars 2 forks source link

Multi-Type Timelines #43

Closed Lavaeolous closed 2 months ago

Lavaeolous commented 5 months ago

As another user noted in the original repo (https://github.com/Darakah/obsidian-timelines/issues/67) it would be nice to add combinable timelines.

My use case would be a timeline containing multiple types of events, the filtering of applicable events should then happen if ANY of the tags noted in the timeline block are found, not when ALL are found.

Currently, the filtering happens in the method filterMDFiles via

return tagList.every((val) => {
logger(`testing val: ${val}`, fileTags.includes(String(val)));
return fileTags.includes(String(val));
});

Overriding this to use .some() instead of .every() is enough, but we would need a way to trigger this filtering when another separator or something is found. I have no idea if there are any conventions for this, but for readability i think maybe use + for AND and | for OR?

Lavaeolous commented 5 months ago

A pretty brutish way to accomplish this would be to change createTagList such as this:

const createTagList = (tagString, timelineTag) => {
    const tagList = [];

    if (tagString.contains("+")) {
        tagString.split('+').forEach((tag) => {
            return parseTag$2(tag, tagList);
        });
        tagList.push(timelineTag);
    }
    else if (tagString.contains("|")) {

        combinedTimeline = true;

        tagString.split('|').forEach((tag) => {
            return parseTag$2(tag, tagList);
        });
        tagList.push(timelineTag);
    }
    else {
        tagString.split(';').forEach((tag) => {
            return parseTag$2(tag, tagList);
        });
        tagList.push(timelineTag);
    }

    return tagList;
};

Here we set a global variable to true if we detect an "|" as a separator, which in turn we can use to switch between .some and .every in filterMDFiles.

seanlowe commented 5 months ago

Hey @Lavaeolous, thanks for creating the issue. I like the way you're describing this, it should be a decent way to implement this. Let me tinker on it for a bit and see if I can't add it shortly.

Life's a little hectic right now, but I'll put aside some time to work on it soon.

seanlowe commented 5 months ago

OK, I know it's been a while but I've finally had some time to put some thought into this. Here's kinda what I came up with, let me know your thoughts.

Right now, like you mention, we want to match ALL tags via the tagA;tagB format. I think it would be fine to keep this, so how to complement this existing behaviour with the possibility of either/or tags?

Right now my main choice for implementation would look something like this: tagA|tagB;tagC. In which I would look for all notes with tagC and from within those notes, they can have either tagA or tagB as well.

Is this along the lines of what you were thinking?

Lavaeolous commented 4 months ago

Hey, that would do more than i initially thought of (e.g. combining either/or in a single query), which is fine by me! Would this "scale" (for the lack of a better term) to larger queries, e.g. tagA;...;tagA+n|tagB; ... ;tagB+m? Not that i have a use case for that, just out of curiosity.

seanlowe commented 4 months ago

Theoretically, yeah it would scale to whatever level needed. At least, if I write the logic correctly... Hehe that's the kicker, as always

Lavaeolous commented 4 months ago

Thanks, i appreciate your work. Looking forward to the implementation!

seanlowe commented 2 months ago

Hey @Lavaeolous. Checking in because I think I've gotten it to a point where it's pretty much done.

My implementation right now is that if I give it the codeblock:

```ob-timeline
tags=required;optionalA|optionalB

I will see a timeline with events that have the `required` tag *AND either* the tag `optionalA` or `optionalB`.

![image](https://github.com/user-attachments/assets/c106877c-38db-41a2-8ed3-2f461cd6ec1f)

---

Reading back over your initial issue description. It doesn't quite sound like it matches exactly. But if you wanted a timeline that matched any of the supplied tags, you would have a list of only optional tags, like so:
tags=required|optionalA|optionalB


Which would display any events with the `required`, `optionalA`, or `optionalB` tags.
![image](https://github.com/user-attachments/assets/ba45a8a7-bd43-4b5c-8d98-627f9f0500f5)

---

This also works for horizontal timelines:
![image](https://github.com/user-attachments/assets/1b9715ce-b88b-4c57-92e1-90ed3d45d2bd)

---

##### Files used in testing:

"optA":
<details>
  <img src="https://github.com/user-attachments/assets/fa197a4d-8040-4259-83db-d43e4dd754ca">
  </img>
</details>

"optB":
<details>
  <img src="https://github.com/user-attachments/assets/6e5b465e-ed65-4800-a642-6d548adc5320">
  </img>
</details>

"required":
<details>
  <img src="https://github.com/user-attachments/assets/bd24fc49-feb2-41d5-9c66-5005db99b803">
  </img>
</details>
seanlowe commented 2 months ago

This has been added in release 2.3.0