Skip to content

Commit ac2f4af

Browse files
authored
feat(PrismCode): diff support (#569)
1 parent 81e278f commit ac2f4af

File tree

8 files changed

+501
-7
lines changed

8 files changed

+501
-7
lines changed

.changeset/cool-bottles-report.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@cube-dev/ui-kit': minor
3+
---
4+
5+
Add support for diff in PrismCode and add a separate PrismDiffCode component that shows a diff between two strings.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"@vitejs/plugin-react": "^4.3.2",
7373
"clipboard-copy": "^4.0.1",
7474
"clsx": "^1.1.1",
75+
"diff": "^7.0.0",
7576
"email-validator": "^2.0.4",
7677
"prismjs": "^1.27.0",
7778
"react-aria": "^3.35.1",

pnpm-lock.yaml

+12-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/GlobalStyles.tsx

+13-3
Original file line numberDiff line numberDiff line change
@@ -304,10 +304,9 @@ const GlobalStylesElement = createGlobalStyle<GlobalStylesElementProps>`
304304
.token.prolog,
305305
.token.doctype,
306306
.token.cdata {
307-
color: var(--dark-04-color);
307+
color: var(--dark-03-color);
308308
}
309309
310-
.token.keyword,
311310
.token.tag,
312311
.token.operator,
313312
.token.punctuation {
@@ -322,6 +321,8 @@ const GlobalStylesElement = createGlobalStyle<GlobalStylesElementProps>`
322321
.token.boolean,
323322
.token.constant,
324323
.token.symbol,
324+
.token.key,
325+
.token.keyword,
325326
.token.deleted {
326327
color: var(--pink-color);
327328
}
@@ -346,7 +347,6 @@ const GlobalStylesElement = createGlobalStyle<GlobalStylesElementProps>`
346347
color: var(--dark-color);
347348
}
348349
349-
.token.atrule,
350350
.token.attr-value {
351351
color: var(--dark-color);
352352
}
@@ -379,6 +379,16 @@ const GlobalStylesElement = createGlobalStyle<GlobalStylesElementProps>`
379379
.token.entity {
380380
cursor: help;
381381
}
382+
383+
.token.inserted-sign {
384+
background-color: #e6ffed;
385+
color: #30A666;
386+
}
387+
388+
.token.deleted-sign {
389+
background-color: #ffeef0;
390+
color: (--danger-color);
391+
}
382392
`;
383393

384394
export const GlobalStyles = (props: GlobalStylesProps) => {

src/components/content/PrismCode/PrismCode.stories.tsx

+130
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,133 @@ JavascriptSyntax.args = {
7474
}
7575
});`,
7676
};
77+
78+
export const YamlSyntax = Template.bind({});
79+
YamlSyntax.args = {
80+
language: 'yaml',
81+
code: `cubes:
82+
# Define the Orders cube
83+
- name: Orders
84+
sql: SELECT * FROM public.orders
85+
86+
# Measures (metrics to analyze)
87+
measures:
88+
- name: count
89+
type: count # Total number of orders
90+
- name: totalRevenue
91+
sql: total_amount
92+
type: sum # Sum of all order totals
93+
94+
# Dimensions (categorical or time-based data)
95+
dimensions:
96+
- name: id
97+
sql: id
98+
type: number # Unique order ID
99+
- name: status
100+
sql: status
101+
type: string # Order status (e.g., completed, pending)
102+
- name: created_at
103+
sql: created_at
104+
type: time # Order creation date
105+
106+
# Pre-aggregation for performance optimization
107+
preAggregations:
108+
monthlyRevenue:
109+
type: rollup
110+
measures: [totalRevenue] # Aggregate total revenue
111+
dimensions: [status] # Group by order status
112+
timeDimension: created_at
113+
granularity: month # Monthly rollups
114+
115+
# Define the Customers cube
116+
- name: Customers
117+
sql: SELECT * FROM public.customers
118+
119+
# Measures
120+
measures:
121+
- name: totalCustomers
122+
type: countDistinct
123+
sql: id # Count distinct customer IDs
124+
125+
# Dimensions
126+
dimensions:
127+
- name: id
128+
sql: id
129+
type: number # Customer ID
130+
- name: name
131+
sql: name
132+
type: string # Customer name
133+
134+
# Join with the Orders cube
135+
joins:
136+
- cube: Orders
137+
sql: \${Customers.id} = \${Orders.customer_id}
138+
relationship: one_to_many # One customer can have many orders`,
139+
};
140+
141+
export const SqlSyntax = Template.bind({});
142+
SqlSyntax.args = {
143+
language: 'sql',
144+
code: `WITH RecursiveCTE AS (
145+
-- Recursive CTE to generate a sequence of numbers
146+
SELECT 1 AS Level, CAST('2025-01-01' AS DATE) AS GeneratedDate
147+
UNION ALL
148+
SELECT Level + 1, DATEADD(DAY, 1, GeneratedDate)
149+
FROM RecursiveCTE
150+
WHERE Level < 10
151+
),
152+
AggregatedData AS (
153+
-- Aggregate data with window functions and filters
154+
SELECT
155+
u.UserID,
156+
u.UserName,
157+
COUNT(o.OrderID) OVER (PARTITION BY u.UserID) AS TotalOrders,
158+
SUM(o.TotalAmount) OVER (PARTITION BY u.UserID) AS TotalSpent,
159+
ROW_NUMBER() OVER (PARTITION BY u.UserID ORDER BY o.OrderDate DESC) AS LatestOrderRank
160+
FROM Users u
161+
LEFT JOIN Orders o ON u.UserID = o.UserID
162+
WHERE o.OrderDate > '2024-01-01'
163+
),
164+
FilteredData AS (
165+
-- Filter the aggregated data to the most recent order per user
166+
SELECT *
167+
FROM AggregatedData
168+
WHERE LatestOrderRank = 1
169+
),
170+
FinalOutput AS (
171+
-- Final output with additional computations
172+
SELECT
173+
f.UserID,
174+
f.UserName,
175+
f.TotalOrders,
176+
f.TotalSpent,
177+
CASE
178+
WHEN f.TotalSpent > 1000 THEN 'VIP'
179+
WHEN f.TotalSpent BETWEEN 500 AND 1000 THEN 'Regular'
180+
ELSE 'New'
181+
END AS UserCategory,
182+
r.GeneratedDate
183+
FROM FilteredData f
184+
CROSS JOIN RecursiveCTE r
185+
WHERE r.GeneratedDate <= GETDATE()
186+
)
187+
-- Final query to output the results
188+
SELECT
189+
fo.UserID,
190+
fo.UserName,
191+
fo.TotalOrders,
192+
fo.TotalSpent,
193+
fo.UserCategory,
194+
fo.GeneratedDate
195+
FROM FinalOutput fo
196+
ORDER BY fo.GeneratedDate, fo.UserID;`,
197+
};
198+
199+
export const DiffSyntax = Template.bind({});
200+
DiffSyntax.args = {
201+
language: 'javascript',
202+
code: ` console.log('Hello, world!');
203+
+ console.log('This line was added!');
204+
console.log('Another unchanged line');
205+
- console.log('This line was removed.');`,
206+
};

src/components/content/PrismCode/PrismCode.tsx

+35-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { forwardRef, useEffect } from 'react';
22
import Prism from 'prismjs';
3+
import 'prismjs/components/prism-yaml';
4+
import 'prismjs/components/prism-sql';
5+
import 'prismjs/components/prism-javascript';
6+
import 'prismjs/components/prism-diff';
7+
import 'prismjs/plugins/diff-highlight/prism-diff-highlight';
38

49
import {
510
BaseProps,
@@ -26,6 +31,30 @@ const PreElement = tasty({
2631
},
2732
});
2833

34+
function isDiffCode(code: string): boolean {
35+
// Split the code into lines
36+
const lines = code.split('\n');
37+
38+
// Define patterns to check for diff characteristics
39+
const additionPattern = /^\+/; // Lines starting with '+'
40+
const deletionPattern = /^-/; // Lines starting with '-'
41+
const headerPattern = /^(diff --git|---|\+\+\+)/; // Diff headers
42+
43+
// Check each line for diff-specific patterns
44+
for (const line of lines) {
45+
if (
46+
additionPattern.test(line) ||
47+
deletionPattern.test(line) ||
48+
headerPattern.test(line)
49+
) {
50+
return true; // Code matches a diff pattern
51+
}
52+
}
53+
54+
// No diff-specific patterns found
55+
return false;
56+
}
57+
2958
export interface CubePrismCodeProps extends ContainerStyleProps {
3059
/** The CSS style map */
3160
style?: BaseProps['style'];
@@ -51,13 +80,18 @@ export interface CubePrismCodeProps extends ContainerStyleProps {
5180
function PrismCode(props: CubePrismCodeProps, ref) {
5281
let { code, language = 'javascript', ...otherProps } = props;
5382

83+
const isDiff = isDiffCode(code || '');
84+
5485
useEffect(() => {
5586
Prism.highlightAll();
5687
});
5788

5889
return (
5990
<PreElement ref={ref} {...otherProps}>
60-
<code data-element="Code" className={`language-${language}`}>
91+
<code
92+
data-element="Code"
93+
className={`language${isDiff ? '-diff' : ''}-${language}${isDiff ? ' diff-highlight' : ''}`}
94+
>
6195
{code}
6296
</code>
6397
</PreElement>

0 commit comments

Comments
 (0)