#meta
Implements some useful general purpose widgets. Specifically:
Types of button widgets:
widgets.button(text, callback) renders a simple button running the callback when clickedwidgets.commandButton(commandName) renders a button for a particular command (where the button text is the command name itself)widgets.commandButton(text, commandName) renders a button for a particular command with a custom button textwidgets.commandButton(text, commandName, args) renders a button for a particular command and arguments (specified as a table list) with a custom button textExamples:
${widgets.button("Hello", function() editor.flashNotification "Hi there!" end)}
${widgets.commandButton("System: Reload")}
These can each be individually enabled/disabled and configured in your CONFIG page (use space-lua instead of lua):
-- Disable TOC altogether
config.set("std.widgets.toc.enabled", false)
-- Only render a TOC when there's >= 5 headers
config.set("std.widgets.toc.minHeaders", 5)
-- Disable linked mentions altogether
config.set("std.widgets.linkedMentions.enabled", false)
-- Disable linked tasks altogether
config.set("std.widgets.linkedTasks.enabled", false)
-- priority: 10
function widgets.button(text, callback, attrs)
local buttonEl = {
onclick = callback,
text
}
-- attrs can be used for additional customization
if attrs then
for k, v in pairs(attrs) do
buttonEl[k] = v
end
end
return widget.html(dom.button(buttonEl))
end
function widgets.commandButton(text, commandName, args)
if not commandName then
-- When only passed one argument, then let's assume it's a command name
commandName = text
end
return widget.html(dom.button {
onclick = function()
editor.invokeCommand(commandName, args)
end,
text
})
end
function widgets.subPages(pageName)
local prefix = (pageName or editor.getCurrentPage()) .. "/"
return widget.markdown(template.each(query[
from index.tag "page"
where string.startsWith(_.name, prefix)
](
from index.tag "page"
where string.startsWith(_.name, prefix)
), templates.pageItem))
end
-- priority: 10
widgets = widgets or {}
-- configuration schema
config.define("std.widgets.toc", {
type = "object",
properties = {
enabled = schema.boolean(),
minHeaders = schema.number(),
}
})
-- configuration default values
config.set("std.widgets.toc", {
enabled = true,
minHeaders = 3
})
function widgets.toc(options)
options = options or config.get("std.widgets.toc")
options.minHeaders = options.minHeaders or 3
local text = editor.getText()
local pageName = editor.getCurrentPage()
local parsedMarkdown = markdown.parseMarkdown(text)
-- Collect all headers
local headers = {}
for topLevelChild in parsedMarkdown.children do
if topLevelChild.type then
local headerLevel = string.match(topLevelChild.type, "^ATXHeading(%d+)")
if headerLevel then
local text = ""
table.remove(topLevelChild.children, 1)
for child in topLevelChild.children do
text = text .. string.trim(markdown.renderParseTree(child))
end
-- Strip link syntax to avoid nested brackets in TOC
text = string.gsub(text, "%[%[(.-)%]%]", "%1")
if text != "" then
table.insert(headers, {
name = text,
pos = topLevelChild.from,
level = tonumber(headerLevel)
})
end
end
end
end
if options.minHeaders and options.minHeaders > #headers then
return widget.new{}
end
-- Find min level
local minLevel = 6
for _, header in ipairs(headers) do
if header.level < minLevel then
minLevel = header.level
end
end
-- Build up markdown
local md = (options.header or "# Table of Contents") .. "\n"
for _, header in ipairs(headers) do
if not(options.maxHeader and header.level > options.maxHeader or
options.minLevel and header.level < options.minLevel) then
md = md .. string.rep(" ", (header.level - minLevel) * 2) ..
"* [" .. pageName .. "@" .. header.pos .. "|" .. header.name .. "](" .. pageName .. "@" .. header.pos .. "|" .. header.name .. ")\n"
end
end
return widget.new {
markdown = md
}
end
-- priority: -1
if config.get("std.widgets.toc.enabled") then
event.listen {
name = "hooks:renderTopWidgets",
run = function(e)
local pageText = editor.getText()
local fm = index.extractFrontmatter(pageText)
if fm.frontmatter.pageDecoration and fm.frontmatter.pageDecoration.disableTOC then
return
end
return widgets.toc()
end
}
end
-- priority: 10
widgets = widgets or {}
local mentionTemplate = template.new [==[
**[${_.ref}](${_.ref})**:
${_.snippet}
]==]
-- configuration schema
config.define("std.widgets.linkedMentions", {
type = "object",
properties = {
enabled = schema.boolean(),
}
})
-- configuration default values
config.set("std.widgets.linkedMentions", {
enabled = true,
})
function widgets.linkedMentions(pageName)
pageName = pageName or editor.getCurrentPage()
local linkedMentions = query[
from index.tag "link"
where _.page != pageName and _.toPage == pageName
order by _.page desc, _.pos
](
from index.tag "link"
where _.page != pageName and _.toPage == pageName
order by _.page desc, _.pos
)
if #linkedMentions > 0 then
return widget.new {
markdown = "# Linked Mentions\n"
.. template.each(linkedMentions, mentionTemplate)
}
end
end
-- priority: -1
if config.get("std.widgets.linkedMentions.enabled") then
event.listen {
name = "hooks:renderBottomWidgets",
run = function(e)
return widgets.linkedMentions()
end
}
end
-- priority: 10
-- configuration schema
config.define("std.widgets.linkedTasks", {
type = "object",
properties = {
enabled = schema.boolean(),
}
})
-- configuration default values
config.set("std.widgets.linkedTasks", {
enabled = true,
})
function widgets.linkedTasks(pageName)
pageName = pageName or editor.getCurrentPage()
local tasks = query[
from index.tag "task"
where not _.done and table.includes(_.ilinks, pageName)
order by _.page
](
from index.tag "task"
where not _.done and table.includes(_.ilinks, pageName)
order by _.page
)
local md = ""
if #tasks > 0 then
md = "# Linked Tasks\n"
.. template.each(tasks, templates.taskItem)
else
md = ""
end
return widget.new {
markdown = md
}
end
-- priority: -1
if config.get("std.widgets.linkedTasks.enabled") then
event.listen {
name = "hooks:renderTopWidgets",
run = function(e)
return widgets.linkedTasks()
end
}
end