poetry-charts 0.0.2
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.
- checksums.yaml +7 -0
- data/.gitattributes +6 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +94 -0
- data/THIRD_PARTY_NOTICES.md +39 -0
- data/app/assets/stylesheets/poetry-charts.css +117 -0
- data/app/components/poetry/charts/adapter_chart/component.html.erb +6 -0
- data/app/components/poetry/charts/adapter_chart/component.rb +99 -0
- data/app/components/poetry/charts/adapter_chart/preview.rb +37 -0
- data/app/components/poetry/charts/adapter_chart/style.rb +16 -0
- data/app/components/poetry/charts/area_chart/component.html.erb +75 -0
- data/app/components/poetry/charts/area_chart/component.rb +251 -0
- data/app/components/poetry/charts/area_chart/preview.rb +95 -0
- data/app/components/poetry/charts/area_chart/style.rb +29 -0
- data/app/components/poetry/charts/bar_chart/component.html.erb +95 -0
- data/app/components/poetry/charts/bar_chart/component.rb +318 -0
- data/app/components/poetry/charts/bar_chart/preview.rb +115 -0
- data/app/components/poetry/charts/bar_chart/style.rb +28 -0
- data/app/components/poetry/charts/bar_math.rb +95 -0
- data/app/components/poetry/charts/cartesian_family.rb +105 -0
- data/app/components/poetry/charts/chart_family.rb +58 -0
- data/app/components/poetry/charts/composed_chart/component.html.erb +95 -0
- data/app/components/poetry/charts/composed_chart/component.rb +309 -0
- data/app/components/poetry/charts/composed_chart/preview.rb +62 -0
- data/app/components/poetry/charts/composed_chart/style.rb +24 -0
- data/app/components/poetry/charts/container/component.html.erb +4 -0
- data/app/components/poetry/charts/container/component.rb +80 -0
- data/app/components/poetry/charts/container/preview.rb +45 -0
- data/app/components/poetry/charts/container/style.rb +22 -0
- data/app/components/poetry/charts/error_bars.rb +35 -0
- data/app/components/poetry/charts/legend_content/component.html.erb +10 -0
- data/app/components/poetry/charts/legend_content/component.rb +122 -0
- data/app/components/poetry/charts/legend_content/preview.rb +23 -0
- data/app/components/poetry/charts/legend_content/style.rb +26 -0
- data/app/components/poetry/charts/line_chart/component.html.erb +85 -0
- data/app/components/poetry/charts/line_chart/component.rb +252 -0
- data/app/components/poetry/charts/line_chart/preview.rb +115 -0
- data/app/components/poetry/charts/line_chart/style.rb +28 -0
- data/app/components/poetry/charts/live.rb +296 -0
- data/app/components/poetry/charts/motion.rb +98 -0
- data/app/components/poetry/charts/pie_chart/component.html.erb +36 -0
- data/app/components/poetry/charts/pie_chart/component.rb +256 -0
- data/app/components/poetry/charts/pie_chart/preview.rb +100 -0
- data/app/components/poetry/charts/pie_chart/style.rb +19 -0
- data/app/components/poetry/charts/polar_family.rb +95 -0
- data/app/components/poetry/charts/radar_chart/component.html.erb +73 -0
- data/app/components/poetry/charts/radar_chart/component.rb +329 -0
- data/app/components/poetry/charts/radar_chart/preview.rb +97 -0
- data/app/components/poetry/charts/radar_chart/style.rb +21 -0
- data/app/components/poetry/charts/radial_bar_chart/component.html.erb +65 -0
- data/app/components/poetry/charts/radial_bar_chart/component.rb +424 -0
- data/app/components/poetry/charts/radial_bar_chart/preview.rb +99 -0
- data/app/components/poetry/charts/radial_bar_chart/style.rb +27 -0
- data/app/components/poetry/charts/reference_marks.rb +156 -0
- data/app/components/poetry/charts/scatter_chart/component.html.erb +55 -0
- data/app/components/poetry/charts/scatter_chart/component.rb +396 -0
- data/app/components/poetry/charts/scatter_chart/preview.rb +93 -0
- data/app/components/poetry/charts/scatter_chart/style.rb +22 -0
- data/app/components/poetry/charts/tooltip_content/component.html.erb +19 -0
- data/app/components/poetry/charts/tooltip_content/component.rb +168 -0
- data/app/components/poetry/charts/tooltip_content/preview.rb +39 -0
- data/app/components/poetry/charts/tooltip_content/style.rb +31 -0
- data/app/components/poetry/charts/tooltip_layer/component.html.erb +6 -0
- data/app/components/poetry/charts/tooltip_layer/component.rb +61 -0
- data/app/components/poetry/charts/tooltip_layer/style.rb +13 -0
- data/app/components/poetry/charts/tooltip_wiring.rb +167 -0
- data/app/helpers/poetry/charts/components_helper.rb +125 -0
- data/app/javascript/poetry/charts/adapter_controller.js +75 -0
- data/app/javascript/poetry/charts/adapter_registry.js +52 -0
- data/app/javascript/poetry/charts/adapters/chartjs.js +86 -0
- data/app/javascript/poetry/charts/d3.js +14 -0
- data/app/javascript/poetry/charts/index.js +67 -0
- data/app/javascript/poetry/charts/live/renderer.js +456 -0
- data/app/javascript/poetry/charts/live_controller.js +195 -0
- data/app/javascript/poetry/charts/motion/flip.js +169 -0
- data/app/javascript/poetry/charts/motion/sector.js +62 -0
- data/app/javascript/poetry/charts/motion/tween.js +105 -0
- data/app/javascript/poetry/charts/motion_controller.js +230 -0
- data/app/javascript/poetry/charts/tooltip_controller.js +331 -0
- data/app/javascript/poetry/charts/window_controller.js +195 -0
- data/config/component_descriptions.yml +14 -0
- data/config/component_registry.yml +3055 -0
- data/config/controllers_manifest.json +100 -0
- data/config/importmap.rb +10 -0
- data/lib/poetry/charts/cartesian.rb +277 -0
- data/lib/poetry/charts/config.rb +149 -0
- data/lib/poetry/charts/engine.rb +76 -0
- data/lib/poetry/charts/geometry/area.rb +68 -0
- data/lib/poetry/charts/geometry/curve.rb +327 -0
- data/lib/poetry/charts/geometry/line.rb +64 -0
- data/lib/poetry/charts/geometry/nice_ticks.rb +220 -0
- data/lib/poetry/charts/geometry/path.rb +96 -0
- data/lib/poetry/charts/geometry/scale/band.rb +81 -0
- data/lib/poetry/charts/geometry/scale/linear.rb +127 -0
- data/lib/poetry/charts/geometry/stack.rb +129 -0
- data/lib/poetry/charts/geometry/ticks.rb +103 -0
- data/lib/poetry/charts/geometry.rb +66 -0
- data/lib/poetry/charts/polar.rb +179 -0
- data/lib/poetry/charts/spec.rb +108 -0
- data/lib/poetry/charts/theme_style.rb +53 -0
- data/lib/poetry/charts/version.rb +8 -0
- data/lib/poetry/charts.rb +114 -0
- data/lib/poetry-charts.rb +4 -0
- data/themes/default.css +146 -0
- data/themes/luma.css +139 -0
- data/themes/lyra.css +140 -0
- data/themes/maia.css +139 -0
- data/themes/mira.css +139 -0
- data/themes/nova.css +139 -0
- data/themes/rhea.css +139 -0
- data/themes/sera.css +140 -0
- data/themes/vega.css +144 -0
- data/vendor/d3-kernel/LICENSE-d3.txt +17 -0
- data/vendor/d3-kernel/LICENSE-decimal.js-light.txt +25 -0
- data/vendor/d3-kernel/LICENSE-recharts.txt +26 -0
- data/vendor/d3-kernel/README.md +30 -0
- data/vendor/d3-kernel/build.mjs +66 -0
- data/vendor/d3-kernel/entry.js +27 -0
- data/vendor/d3-kernel/recharts/state/cartesianAxisSlice.ts +6 -0
- data/vendor/d3-kernel/recharts/util/scale/getNiceTickValues.ts +310 -0
- data/vendor/d3-kernel/recharts/util/scale/util/arithmetic.ts +54 -0
- data/vendor/d3-kernel/recharts/util/types.ts +5 -0
- metadata +186 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Charts
|
|
5
|
+
# The chart-spec: the CLOSED, VERSIONED description every poetry chart
|
|
6
|
+
# compiles to. The server engine consumes it Ruby-side; swappable
|
|
7
|
+
# adapters consume the same spec JSON-side through the duck-typed
|
|
8
|
+
# protocol (render(el, spec) / update / destroy). No engine-specific key
|
|
9
|
+
# ever enters this schema - engine styling lives in the adapter,
|
|
10
|
+
# declared; a pass-through options bag would tie call sites to one
|
|
11
|
+
# engine and the spec could never close.
|
|
12
|
+
#
|
|
13
|
+
# Keys ride the wire camelCased (dataKey, stackId) - one vocabulary
|
|
14
|
+
# on both sides of the seam.
|
|
15
|
+
#
|
|
16
|
+
# @example Compile a spec and serialize it for an adapter
|
|
17
|
+
# Poetry::Charts::Spec.new(
|
|
18
|
+
# type: :bar, data: rows, series: [{ data_key: :revenue }]
|
|
19
|
+
# ).to_json
|
|
20
|
+
class Spec
|
|
21
|
+
# The spec schema version stamped into every payload.
|
|
22
|
+
VERSION = 1
|
|
23
|
+
|
|
24
|
+
# The chart types the spec can describe.
|
|
25
|
+
TYPES = %i[area bar line pie radar radial].freeze
|
|
26
|
+
|
|
27
|
+
# The closed key set a series entry may carry.
|
|
28
|
+
SERIES_KEYS = %i[key data_key name_key stack curve].freeze
|
|
29
|
+
# The closed key set an axis entry may carry.
|
|
30
|
+
AXIS_KEYS = %i[data_key hide tick_count format].freeze
|
|
31
|
+
|
|
32
|
+
attr_reader :type, :data, :series, :axes, :config
|
|
33
|
+
|
|
34
|
+
# @param type [Symbol, String] one of TYPES
|
|
35
|
+
# @param data [Array<Hash>] the rows, one hash per data point
|
|
36
|
+
# @param series [Array<Hash>] series entries; data_key: is required
|
|
37
|
+
# @param axes [Hash] per-axis settings (SERIES_KEYS/AXIS_KEYS closed sets)
|
|
38
|
+
# @param config [Config, Hash, nil] label/color config, wrapped via Config.wrap
|
|
39
|
+
def initialize(type:, data:, series:, axes: {}, config: nil)
|
|
40
|
+
@type = type.to_sym
|
|
41
|
+
unless TYPES.include?(@type)
|
|
42
|
+
raise ArgumentError,
|
|
43
|
+
"unknown chart type #{type.inspect} (one of #{TYPES.join(", ")})"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
@data = Array(data)
|
|
47
|
+
@series = normalize_series(series)
|
|
48
|
+
@axes = normalize_axes(axes)
|
|
49
|
+
@config = Config.wrap(config)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# The wire form: string keys, camelCased entry keys, the version
|
|
53
|
+
# stamped in.
|
|
54
|
+
#
|
|
55
|
+
# @return [Hash]
|
|
56
|
+
def to_h
|
|
57
|
+
{
|
|
58
|
+
"version" => VERSION,
|
|
59
|
+
"type" => type.to_s,
|
|
60
|
+
"data" => data.map { |row| row.to_h.transform_keys(&:to_s) },
|
|
61
|
+
"series" => series.map { |entry| camelize_keys(entry) },
|
|
62
|
+
"axes" => axes.to_h { |name, axis| [name.to_s, camelize_keys(axis)] },
|
|
63
|
+
"config" => config.to_h.transform_values { |value| camelize_keys(value) }
|
|
64
|
+
}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# The wire form serialized - what the spec <script> embeds.
|
|
68
|
+
#
|
|
69
|
+
# @return [String]
|
|
70
|
+
def to_json(...)
|
|
71
|
+
to_h.to_json(...)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def normalize_series(series)
|
|
77
|
+
Array(series).map do |entry|
|
|
78
|
+
entry = entry.symbolize_keys
|
|
79
|
+
raise ArgumentError, "a series entry requires data_key: (got #{entry.inspect})" unless entry[:data_key]
|
|
80
|
+
|
|
81
|
+
unknown = entry.keys - SERIES_KEYS
|
|
82
|
+
raise ArgumentError, "unknown series keys #{unknown.inspect} (the spec is closed)" if unknown.any?
|
|
83
|
+
|
|
84
|
+
{ key: entry[:data_key].to_s }.merge(entry).compact
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def normalize_axes(axes)
|
|
89
|
+
axes.symbolize_keys.to_h do |name, axis|
|
|
90
|
+
axis = axis.symbolize_keys
|
|
91
|
+
unknown = axis.keys - AXIS_KEYS
|
|
92
|
+
raise ArgumentError, "unknown axis keys #{unknown.inspect} on #{name} (the spec is closed)" if unknown.any?
|
|
93
|
+
|
|
94
|
+
[name, axis]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# snake_case Ruby -> camelCase wire (dataKey, stackId-style).
|
|
99
|
+
# Symbol values (data keys, curve names) become strings on the
|
|
100
|
+
# wire.
|
|
101
|
+
def camelize_keys(hash)
|
|
102
|
+
hash.to_h do |key, value|
|
|
103
|
+
[key.to_s.camelize(:lower), value.is_a?(Symbol) ? value.to_s : value]
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Poetry
|
|
4
|
+
module Charts
|
|
5
|
+
# Turns a chart's Config into the scoped per-series custom
|
|
6
|
+
# properties, one block per theme -
|
|
7
|
+
#
|
|
8
|
+
# [data-chart=chart-revenue] { --color-desktop: var(--chart-1); }
|
|
9
|
+
# .dark [data-chart=chart-revenue] { --color-desktop: oklch(...); }
|
|
10
|
+
#
|
|
11
|
+
# so series markup (SVG fills, tooltip indicators, legend swatches) can
|
|
12
|
+
# reference var(--color-<key>) and follow theme flips with ZERO
|
|
13
|
+
# re-render. Emission is safe by construction: Config validated every
|
|
14
|
+
# key and color at wrap time.
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# Poetry::Charts::ThemeStyle.new(id: "chart-revenue", config: config).css
|
|
18
|
+
class ThemeStyle
|
|
19
|
+
# Theme name -> selector prefix (the .dark class convention).
|
|
20
|
+
THEMES = { light: "", dark: ".dark " }.freeze
|
|
21
|
+
|
|
22
|
+
# @param id [String] the container's data-chart identifier
|
|
23
|
+
# @param config [Config, Hash] the chart config the colors come from
|
|
24
|
+
def initialize(id:, config:)
|
|
25
|
+
@id = id
|
|
26
|
+
@config = Config.wrap(config)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# The stylesheet text, or nil when no entry carries a color - a
|
|
30
|
+
# colorless config renders no <style> element at all.
|
|
31
|
+
#
|
|
32
|
+
# @return [String, nil]
|
|
33
|
+
def css
|
|
34
|
+
entries = @config.color_entries
|
|
35
|
+
return nil if entries.empty?
|
|
36
|
+
|
|
37
|
+
THEMES.filter_map { |theme, prefix| theme_block(theme, prefix, entries) }.join("\n")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def theme_block(theme, prefix, entries)
|
|
43
|
+
declarations = entries.filter_map do |entry|
|
|
44
|
+
color = entry.color_for(theme)
|
|
45
|
+
" --color-#{entry.key}: #{color};" if color
|
|
46
|
+
end
|
|
47
|
+
return nil if declarations.empty?
|
|
48
|
+
|
|
49
|
+
"#{prefix}[data-chart=#{@id}] {\n#{declarations.join("\n")}\n}"
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "poetry/core"
|
|
4
|
+
require "yaml"
|
|
5
|
+
require_relative "charts/version"
|
|
6
|
+
require_relative "charts/config"
|
|
7
|
+
require_relative "charts/theme_style"
|
|
8
|
+
require_relative "charts/spec"
|
|
9
|
+
require_relative "charts/geometry"
|
|
10
|
+
require_relative "charts/cartesian"
|
|
11
|
+
require_relative "charts/polar"
|
|
12
|
+
|
|
13
|
+
# The poetry component family's shared root namespace.
|
|
14
|
+
module Poetry
|
|
15
|
+
# poetry's chart tier: charts as server-rendered SVG. Ruby runs the
|
|
16
|
+
# whole geometry pipeline - data -> domains -> scales -> ticks ->
|
|
17
|
+
# points -> paths, with decimal-exact nice ticks - and the finished
|
|
18
|
+
# chart ships in the initial HTML: no-JS/print/email valid, themed by
|
|
19
|
+
# CSS variables (--chart-1..5 + per-chart --color-<key>), dark mode
|
|
20
|
+
# with zero re-render. Stimulus chrome adds tooltip/legend/active
|
|
21
|
+
# interactivity by reading SERVER-EMBEDDED coordinates - no chart math
|
|
22
|
+
# in the browser.
|
|
23
|
+
#
|
|
24
|
+
# Engines stay swappable (three doors): the container contract is
|
|
25
|
+
# engine-agnostic; every chart also compiles to a closed, VERSIONED
|
|
26
|
+
# chart-spec consumed by duck-typed adapters (render/update/destroy -
|
|
27
|
+
# a canvas adapter ships as the reference); client-rendered chart
|
|
28
|
+
# libraries remain reachable through a Stimulus-mounted island.
|
|
29
|
+
#
|
|
30
|
+
# @example Render a chart through the dispatcher helper
|
|
31
|
+
# poetry_chart :bar, data: rows, series: [{ data_key: :revenue }]
|
|
32
|
+
module Charts
|
|
33
|
+
# The curve interpolation whitelist every series slot validates
|
|
34
|
+
# against.
|
|
35
|
+
CURVES = %i[natural linear step step_before step_after monotone_x].freeze
|
|
36
|
+
# The axis capture shape the cartesian family slots accumulate into
|
|
37
|
+
# (scatter carries its own - both axes numeric, different fields).
|
|
38
|
+
AxisConfig = Data.define(:data_key, :tick_formatter, :tick_margin, :tick_count)
|
|
39
|
+
# The grid capture shape - which rule directions render (radar
|
|
40
|
+
# carries its own, with polygon/circle rings).
|
|
41
|
+
GridConfig = Data.define(:vertical, :horizontal)
|
|
42
|
+
|
|
43
|
+
class << self
|
|
44
|
+
# Gem root (the directory containing lib/, app/, config/).
|
|
45
|
+
def root
|
|
46
|
+
@root ||= Pathname.new(File.expand_path("../..", __dir__))
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# The registry builder this gem commits from (the poetry-ui shared-
|
|
50
|
+
# builder rule: rake registry:generate/verify and the sync test share
|
|
51
|
+
# ONE construction). helper_args carries each poetry_* helper's max
|
|
52
|
+
# positional arity from its real signature - poetry_chart(type, ...)
|
|
53
|
+
# legitimately takes one, which is exactly why arity is an emitted
|
|
54
|
+
# per-helper fact and never a convention.
|
|
55
|
+
def registry
|
|
56
|
+
# The helpers section carries the dispatcher's yields declaration:
|
|
57
|
+
# poetry_chart(type) routes to a component that yields its slot
|
|
58
|
+
# builder, so its block param is legitimate - the one exception to
|
|
59
|
+
# the no-wrapper-yields invariant.
|
|
60
|
+
Poetry::Core::Registry.new(
|
|
61
|
+
source_root: root, helper_args: registry_helper_args,
|
|
62
|
+
helpers: { "poetry_chart" => { "yields" => "the dispatched chart component" } },
|
|
63
|
+
descriptions: registry_descriptions
|
|
64
|
+
)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# The editorial per-chart descriptions merged into the registry
|
|
68
|
+
# (component_path => one-liner, from config/component_descriptions.yml).
|
|
69
|
+
# Absent file -> nil, so the registry stays lint-identical without it.
|
|
70
|
+
def registry_descriptions
|
|
71
|
+
path = root.join("config/component_descriptions.yml")
|
|
72
|
+
path.exist? ? YAML.safe_load_file(path) : nil
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# The installable-item projection, boot-free from the COMMITTED
|
|
76
|
+
# registry - the docs site aggregates this with poetry-ui's for
|
|
77
|
+
# /r/*.json.
|
|
78
|
+
def registry_items
|
|
79
|
+
Poetry::Core::RegistryItems.new(
|
|
80
|
+
registry: YAML.safe_load_file(root.join(Poetry::Core::Registry::RELATIVE_PATH)),
|
|
81
|
+
root: root, gem_name: "poetry-charts", gem_version: VERSION
|
|
82
|
+
)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Parameter kinds that count toward a helper's positional arity.
|
|
86
|
+
POSITIONAL_PARAM_KINDS = %i[req opt].freeze
|
|
87
|
+
|
|
88
|
+
# Each poetry_* helper's max positional arity, read from its real
|
|
89
|
+
# signature.
|
|
90
|
+
# @api private
|
|
91
|
+
def registry_helper_args
|
|
92
|
+
require root.join("app/helpers/poetry/charts/components_helper.rb")
|
|
93
|
+
ComponentsHelper.public_instance_methods(false).grep(/\Apoetry_/).sort.filter_map do |name|
|
|
94
|
+
params = ComponentsHelper.instance_method(name).parameters
|
|
95
|
+
next if params.any? { |kind, _param| kind == :rest }
|
|
96
|
+
|
|
97
|
+
[name.to_s, params.count { |kind, _param| POSITIONAL_PARAM_KINDS.include?(kind) }]
|
|
98
|
+
end.to_h
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# The tooltip display string shared by every chart family (matches
|
|
102
|
+
# TooltipContent's Row: delimited numerics from RAW values so
|
|
103
|
+
# integers stay integers, verbatim strings, nil for missing).
|
|
104
|
+
def display_value(value)
|
|
105
|
+
return nil if value.nil?
|
|
106
|
+
return ActiveSupport::NumberHelper.number_to_delimited(value) if value.is_a?(Numeric)
|
|
107
|
+
|
|
108
|
+
value.to_s
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
require_relative "charts/engine"
|
data/themes/default.css
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/* poetry-charts default theme — the cn-* named-class layer.
|
|
2
|
+
*
|
|
3
|
+
* Same contract as poetry-ui's themes/default.css: imported layer(base),
|
|
4
|
+
* dictionaries emit the names + structural classes, this file carries the
|
|
5
|
+
* design. The chart chrome names are SHARED across families (one
|
|
6
|
+
* .cn-chart-grid-line serves every cartesian family) — the equivalent of
|
|
7
|
+
* upstream's [&_.recharts-*] override chains, landed on parts poetry owns.
|
|
8
|
+
* Series fills/strokes stay var(--color-<key>) ATTRIBUTES, never classes;
|
|
9
|
+
* the --chart-1..5 palette rides tokens, not this file.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* MARK: Tooltip */
|
|
13
|
+
.cn-chart-tooltip {
|
|
14
|
+
@apply gap-1.5 rounded-lg border border-border/50 bg-background px-2.5 py-1.5 text-xs shadow-xl;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.cn-chart-tooltip-label {
|
|
18
|
+
@apply font-medium;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.cn-chart-tooltip-row {
|
|
22
|
+
@apply gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.cn-chart-tooltip-name {
|
|
26
|
+
@apply text-muted-foreground;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.cn-chart-tooltip-value {
|
|
30
|
+
@apply font-mono font-medium text-foreground tabular-nums;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* MARK: Legend */
|
|
34
|
+
.cn-chart-legend {
|
|
35
|
+
@apply gap-4;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.cn-chart-legend-align-top {
|
|
39
|
+
@apply pb-3;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.cn-chart-legend-align-bottom {
|
|
43
|
+
@apply pt-3;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.cn-chart-legend-item {
|
|
47
|
+
@apply gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.cn-chart-legend-swatch {
|
|
51
|
+
@apply h-2 w-2 rounded-[2px];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* MARK: Cartesian chrome (shared across families) */
|
|
55
|
+
.cn-chart-grid-line {
|
|
56
|
+
@apply stroke-border/50;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.cn-chart-tick {
|
|
60
|
+
@apply fill-muted-foreground;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.cn-chart-cursor-line {
|
|
64
|
+
@apply stroke-border;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.cn-chart-cursor-band {
|
|
68
|
+
@apply fill-muted;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.cn-chart-reference-line {
|
|
72
|
+
@apply stroke-muted-foreground;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.cn-chart-reference-area {
|
|
76
|
+
@apply fill-muted-foreground;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.cn-chart-reference-dot {
|
|
80
|
+
@apply fill-background stroke-muted-foreground;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.cn-chart-error-bar {
|
|
84
|
+
@apply stroke-foreground;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.cn-chart-brush-track {
|
|
88
|
+
@apply fill-muted;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.cn-chart-brush-window {
|
|
92
|
+
@apply fill-foreground/10 stroke-border;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.cn-chart-brush-handle {
|
|
96
|
+
@apply fill-muted-foreground;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.cn-chart-label {
|
|
100
|
+
@apply fill-foreground;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* MARK: Polar chrome */
|
|
104
|
+
.cn-chart-polar-grid-line {
|
|
105
|
+
@apply fill-none stroke-border;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.cn-chart-polar-grid-circle {
|
|
109
|
+
@apply fill-none stroke-border/50;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.cn-chart-radial-track {
|
|
113
|
+
@apply fill-muted;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.cn-chart-radial-disc-muted {
|
|
117
|
+
@apply fill-muted;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.cn-chart-radial-disc-background {
|
|
121
|
+
@apply fill-background;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.cn-chart-inside-label {
|
|
125
|
+
@apply fill-background text-xs;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.cn-chart-radial-inside-label {
|
|
129
|
+
@apply fill-white capitalize mix-blend-luminosity text-[11px];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.cn-chart-center-title {
|
|
133
|
+
@apply fill-foreground text-3xl font-bold;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.cn-chart-center-title-radial {
|
|
137
|
+
@apply fill-foreground text-4xl font-bold;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.cn-chart-center-title-compact {
|
|
141
|
+
@apply fill-foreground text-2xl font-bold;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.cn-chart-center-subtitle {
|
|
145
|
+
@apply fill-muted-foreground;
|
|
146
|
+
}
|
data/themes/luma.css
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/* poetry-charts luma theme - chart chrome under upstream
|
|
2
|
+
* style-luma.css (pinned clone 1773ecfe, the shadcn@4.19.0 release). Upstream ships ONE chart rule (cn-chart-tooltip); luma reskins it exactly as rhea does (popover surface, ring-for-border, rounded-xl) - everything else carries the default treatment.
|
|
3
|
+
* Same contract as default.css: imported layer(base) by the host entry;
|
|
4
|
+
* POETRY_THEME=luma drives the gates and golden set. */
|
|
5
|
+
/* MARK: Tooltip */
|
|
6
|
+
.cn-chart-tooltip {
|
|
7
|
+
@apply bg-popover text-popover-foreground ring-foreground/5 dark:ring-foreground/10 gap-1.5 rounded-xl px-2.5 py-1.5 text-xs shadow-lg ring-1;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.cn-chart-tooltip-label {
|
|
11
|
+
@apply font-medium;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.cn-chart-tooltip-row {
|
|
15
|
+
@apply gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5 [&>svg]:text-muted-foreground;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.cn-chart-tooltip-name {
|
|
19
|
+
@apply text-muted-foreground;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.cn-chart-tooltip-value {
|
|
23
|
+
@apply font-mono font-medium text-foreground tabular-nums;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* MARK: Legend */
|
|
27
|
+
.cn-chart-legend {
|
|
28
|
+
@apply gap-4;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.cn-chart-legend-align-top {
|
|
32
|
+
@apply pb-3;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.cn-chart-legend-align-bottom {
|
|
36
|
+
@apply pt-3;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.cn-chart-legend-item {
|
|
40
|
+
@apply gap-1.5 [&>svg]:h-3 [&>svg]:w-3 [&>svg]:text-muted-foreground;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.cn-chart-legend-swatch {
|
|
44
|
+
@apply h-2 w-2 rounded-[2px];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* MARK: Cartesian chrome (shared across families) */
|
|
48
|
+
.cn-chart-grid-line {
|
|
49
|
+
@apply stroke-border/50;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.cn-chart-tick {
|
|
53
|
+
@apply fill-muted-foreground;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.cn-chart-cursor-line {
|
|
57
|
+
@apply stroke-border;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.cn-chart-cursor-band {
|
|
61
|
+
@apply fill-muted;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.cn-chart-reference-line {
|
|
65
|
+
@apply stroke-muted-foreground;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.cn-chart-reference-area {
|
|
69
|
+
@apply fill-muted-foreground;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.cn-chart-reference-dot {
|
|
73
|
+
@apply fill-background stroke-muted-foreground;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.cn-chart-error-bar {
|
|
77
|
+
@apply stroke-foreground;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.cn-chart-brush-track {
|
|
81
|
+
@apply fill-muted;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.cn-chart-brush-window {
|
|
85
|
+
@apply fill-foreground/10 stroke-border;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.cn-chart-brush-handle {
|
|
89
|
+
@apply fill-muted-foreground;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.cn-chart-label {
|
|
93
|
+
@apply fill-foreground;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* MARK: Polar chrome */
|
|
97
|
+
.cn-chart-polar-grid-line {
|
|
98
|
+
@apply fill-none stroke-border;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.cn-chart-polar-grid-circle {
|
|
102
|
+
@apply fill-none stroke-border/50;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.cn-chart-radial-track {
|
|
106
|
+
@apply fill-muted;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.cn-chart-radial-disc-muted {
|
|
110
|
+
@apply fill-muted;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.cn-chart-radial-disc-background {
|
|
114
|
+
@apply fill-background;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.cn-chart-inside-label {
|
|
118
|
+
@apply fill-background text-xs;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.cn-chart-radial-inside-label {
|
|
122
|
+
@apply fill-white capitalize mix-blend-luminosity text-[11px];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.cn-chart-center-title {
|
|
126
|
+
@apply fill-foreground text-3xl font-bold;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.cn-chart-center-title-radial {
|
|
130
|
+
@apply fill-foreground text-4xl font-bold;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.cn-chart-center-title-compact {
|
|
134
|
+
@apply fill-foreground text-2xl font-bold;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.cn-chart-center-subtitle {
|
|
138
|
+
@apply fill-muted-foreground;
|
|
139
|
+
}
|