Skip to content

Commit fe250a2

Browse files
committed
chore: add tooltip and popover
1 parent 7ccb224 commit fe250a2

15 files changed

Lines changed: 310 additions & 64 deletions

File tree

ckanext/theming/cli.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def analyze_theme(ctx: click.Context, theme_name: str | None):
136136
templates_path = os.path.join(info.path, "templates")
137137
if os.path.exists(templates_path):
138138
click.secho("\nTemplate files:", fg="yellow")
139-
for root, dirs, files in os.walk(templates_path):
139+
for root, _dirs, files in os.walk(templates_path):
140140
for file in files:
141141
if file.endswith((".html", ".htm", ".xml")):
142142
rel_path = os.path.relpath(os.path.join(root, file), info.path)
@@ -146,7 +146,7 @@ def analyze_theme(ctx: click.Context, theme_name: str | None):
146146
click.echo(click.style(f"Error: Theme '{theme_name}' not found", fg="red"))
147147
click.echo("Available themes:")
148148
themes = lib_theme.collect_themes()
149-
for name in themes.keys():
149+
for name in themes:
150150
click.echo(f" - {name}")
151151

152152

@@ -290,7 +290,7 @@ def check_theme(theme_name: str | None):
290290
click.echo(click.style("✓ Assets directory exists", fg="green"))
291291
# List asset files
292292
asset_files = []
293-
for root, dirs, files in os.walk(assets_dir):
293+
for root, _dirs, files in os.walk(assets_dir):
294294
for file in files:
295295
asset_files.append(os.path.relpath(os.path.join(root, file), assets_dir))
296296
click.echo(f" Assets: {len(asset_files)} files")

ckanext/theming/lib.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import uuid
2323
from collections import defaultdict
2424
from collections.abc import Iterable, Iterator
25-
from typing import Any, Protocol, cast
25+
from typing import Any, Protocol
2626

2727
from jinja2.runtime import Macro
2828
from markupsafe import Markup
@@ -311,11 +311,12 @@ def switch_theme(name: str, config: Any):
311311

312312
next_name = name
313313
enabled_themes = {next_name}
314-
template_paths = []
315314
while theme := themes.get(next_name):
316-
template_paths.append(os.path.join(theme.path, "templates"))
317315
relpath = os.path.relpath(theme.path, os.path.dirname(__file__))
318316

317+
if os.path.isdir(os.path.join(theme.path, "templates")):
318+
tk.add_template_directory(config, os.path.join(relpath, "templates"))
319+
319320
if os.path.isdir(os.path.join(theme.path, "assets")):
320321
tk.add_resource(os.path.join(relpath, "assets"), f"theming/{next_name}")
321322

@@ -335,14 +336,3 @@ def switch_theme(name: str, config: Any):
335336
else:
336337
msg = f"Theme '{next_name}' is not recognised."
337338
raise CkanConfigurationException(msg)
338-
339-
log.info("Re-loading templates from %s", template_paths)
340-
341-
if "plugin_template_paths" in config:
342-
template_paths = cast(list[str], config["plugin_template_paths"]) + template_paths
343-
344-
if extra_template_paths := cast(str, config["extra_template_paths"]):
345-
# must be first for them to override defaults
346-
template_paths = extra_template_paths.split(",") + template_paths
347-
348-
config["computed_template_paths"] = template_paths

ckanext/theming/plugin.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,12 @@
2020

2121
@tk.blanket.cli
2222
@tk.blanket.config_declarations
23-
class ThemingPlugin(ITheme, p.IConfigurer, p.IConfigurable, p.IMiddleware, p.SingletonPlugin):
23+
class ThemingPlugin(ITheme, p.IConfigurer, p.IMiddleware, p.SingletonPlugin):
2424
@override
2525
def update_config(self, config: Any):
26-
# tk.add_template_directory(config, "templates")
27-
# tk.add_public_directory(config, "public")
28-
# tk.add_resource("assets", "theming")
29-
pass
26+
if config["ckan.ui.theme"]:
27+
switch_theme(config["ckan.ui.theme"], config)
28+
UIManager.reset()
3029

3130
@override
3231
def register_themes(self) -> dict[str, Theme]:
@@ -39,12 +38,6 @@ def register_themes(self) -> dict[str, Theme]:
3938
"pico": Theme(os.path.join(root, "themes/pico"), parent="bare"),
4039
}
4140

42-
@override
43-
def configure(self, config: Any) -> None:
44-
if config["ckan.ui.theme"]:
45-
switch_theme(config["ckan.ui.theme"], config)
46-
UIManager.reset()
47-
4841
@override
4942
def make_middleware(self, app: types.CKANApp, config: Any) -> types.CKANApp:
5043
app.jinja_env.add_extension("jinja2.ext.debug")

ckanext/theming/themes/bare/assets/script.js

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,21 @@
4545
constructor(el) {
4646
this.el = el;
4747
}
48-
hide() {
48+
close() {
49+
this.el.hidden = true;
50+
}
51+
show() {
52+
this.el.hidden = false;
53+
}
54+
destroy() {
55+
this.el.remove();
56+
}
57+
}
58+
class Tooltip {
59+
constructor(el) {
60+
this.el = el;
61+
}
62+
close() {
4963
this.el.hidden = true;
5064
}
5165
show() {
@@ -55,6 +69,21 @@
5569
this.el.remove();
5670
}
5771
}
72+
class Popover {
73+
constructor(el) {
74+
this.el = el;
75+
}
76+
close() {
77+
this.el.hidePopover();
78+
}
79+
show() {
80+
document.body.appendChild(this.el);
81+
this.el.showPopover();
82+
}
83+
destroy() {
84+
this.el.remove();
85+
}
86+
}
5887
const ui = {
5988
button(content, params = {}) {
6089
const btn = document.createElement("button");
@@ -137,6 +166,35 @@
137166
}
138167
return new Notification(el);
139168
},
169+
tooltip(content, target, props = {}) {
170+
if (typeof content !== "string") {
171+
throw "Only string tooltips are supported";
172+
}
173+
const el = document.createElement("span");
174+
el.dataset.tooltip = content;
175+
target.insertAdjacentElement("afterend", el);
176+
return new Tooltip(el);
177+
},
178+
getTooltip(id) {
179+
const el = document.getElementById(id);
180+
if (!el) {
181+
return null;
182+
}
183+
return new Tooltip(el);
184+
},
185+
popover(content, props = {}) {
186+
const el = document.createElement("div");
187+
el.popover = "auto";
188+
el.append(content);
189+
return new Popover(el);
190+
},
191+
getPopover(id) {
192+
const el = document.getElementById(id);
193+
if (!el) {
194+
return null;
195+
}
196+
return new Popover(el);
197+
},
140198
};
141199
ckan.sandbox.setup((sb) => {
142200
sb.ui = sb.ui || {};

ckanext/theming/themes/bare/assets/script.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858

5959
class Notification implements INotification {
6060
constructor(public el: HTMLElement) {}
61-
hide() {
61+
close() {
6262
this.el.hidden = true;
6363
}
6464
show() {
@@ -68,6 +68,33 @@
6868
this.el.remove();
6969
}
7070
}
71+
72+
class Tooltip implements ITooltip {
73+
constructor(public el: HTMLElement) {}
74+
close() {
75+
this.el.hidden = true;
76+
}
77+
show() {
78+
this.el.hidden = false;
79+
}
80+
destroy() {
81+
this.el.remove();
82+
}
83+
}
84+
class Popover implements IPopover {
85+
constructor(public el: HTMLElement) {}
86+
close() {
87+
this.el.hidePopover();
88+
}
89+
show() {
90+
document.body.appendChild(this.el);
91+
this.el.showPopover();
92+
}
93+
destroy() {
94+
this.el.remove();
95+
}
96+
}
97+
7198
const ui: IUi = {
7299
button(content: any, params = {}) {
73100
const btn = document.createElement("button");
@@ -173,6 +200,39 @@
173200
}
174201
return new Notification(el);
175202
},
203+
204+
tooltip(content, target, props = {}) {
205+
if (typeof content !== "string") {
206+
throw "Only string tooltips are supported";
207+
}
208+
const el = document.createElement("span");
209+
el.dataset.tooltip = content;
210+
target.insertAdjacentElement("afterend", el);
211+
212+
return new Tooltip(el);
213+
},
214+
215+
getTooltip(id) {
216+
const el = document.getElementById(id);
217+
if (!el) {
218+
return null;
219+
}
220+
return new Tooltip(el);
221+
},
222+
popover(content, props = {}) {
223+
const el = document.createElement("div");
224+
el.popover = "auto";
225+
el.append(content);
226+
return new Popover(el);
227+
},
228+
229+
getPopover(id) {
230+
const el = document.getElementById(id);
231+
if (!el) {
232+
return null;
233+
}
234+
return new Popover(el);
235+
},
176236
};
177237

178238
ckan.sandbox.setup((sb) => {

ckanext/theming/themes/bare/assets/style.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ img {
2626
max-width: 100%;
2727
}
2828

29+
:hover+[data-tooltip]:after {
30+
content: attr(data-tooltip);
31+
padding: 0.5rem;
32+
background: #fff;
33+
position: absolute;
34+
}
35+
2936
#account {
3037
background: var(--account-bg);
3138
text-align: right;

ckanext/theming/themes/bare/templates/macros/ui.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,5 @@
103103
{% set search_form = component.search_form %}
104104
{% set facet_group = component.facet_group %}
105105
{% set facet_item = component.facet_item %}
106+
{% set activity_stream = component.activity_stream %}
107+
{% set activity_item = component.activity_item %}

ckanext/theming/themes/bare/templates/macros/ui/component.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,14 @@
7070
{{ ui.divider() }}
7171
{%- endcall %}
7272
{%- endmacro %}
73+
74+
{%- macro activity_stream(content) -%}
75+
{{ ui.listing(content) }}
76+
{%- endmacro %}
77+
78+
79+
{%- macro activity_item(content, activity) -%}
80+
<div>
81+
{{ content }}
82+
</div>
83+
{%- endmacro %}

ckanext/theming/themes/bare/templates/macros/ui/element.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{%- macro badge(content) -%}
2-
<mark {{ ui.util.attrs(kwargs) }}>{{ content }}</mark>
2+
<sup><mark {{ ui.util.attrs(kwargs) }}>{{ content }}</mark></sup>
33
{%- endmacro %}
44

55
{%- macro button(content, style="primary", type="button") -%}
Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
1-
{% extends "page.html" %}
1+
{% extends "package/read.html" %}
22

3-
{%- block primary_content_inner %}
4-
<h1>{{ _('Activity Stream') }}</h1>
5-
6-
{%- if activities %}
7-
<ul>
8-
{%- for activity in activities %}
9-
<li>{{ activity.activity_type }} - {{ ui.datetime(activity.timestamp) }}</li>
10-
{%- endfor %}
11-
</ul>
3+
{%- block breadcrumb_content -%}
4+
{{ super() }}
5+
{{ ui.breadcrumb_item(_("Activity Stream"), h.url_for("activity.package_activity", id=pkg_dict.name)) }}
6+
{%- endblock %}
127

13-
{{ ui.pagination(page.page, page.last_page, page._url_generator) }}
14-
{%- else %}
15-
<p>{{ _('No activity') }}</p>
16-
{%- endif %}
8+
{%- block primary_content_inner %}
9+
{%- include "snippets/activity_stream.html" -%}
1710
{%- endblock %}

0 commit comments

Comments
 (0)