Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 4 additions & 22 deletions dist/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ const watchApp = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
return {
js: builder_1.ComponentSegment.JavaScript,
scss: builder_1.ComponentSegment.Style,
template: builder_1.ComponentSegment.Previews,
templates: builder_1.ComponentSegment.Previews,
}[type];
};
Expand All @@ -514,12 +515,8 @@ const watchApp = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
case 'unlink':
if (!debounce) {
debounce = true;
let segmentToUpdate = undefined;
const matchingPath = runtimeComponentPathsToWatch.get(file);
if (matchingPath) {
const entryType = runtimeComponentPathsToWatch.get(matchingPath);
segmentToUpdate = entryTypeToSegment(entryType);
}
const entryType = runtimeComponentPathsToWatch.get(file);
const segmentToUpdate = entryType ? entryTypeToSegment(entryType) : undefined;
const componentDir = path_1.default.basename(path_1.default.dirname(path_1.default.dirname(file)));
yield (0, builder_1.default)(handoff, componentDir, segmentToUpdate);
debounce = false;
Expand Down Expand Up @@ -564,7 +561,7 @@ const watchApp = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
if (fs_extra_1.default.existsSync(normalizedComponentEntryPath)) {
const entryType = runtimeComponentEntryType;
if (fs_extra_1.default.statSync(normalizedComponentEntryPath).isFile()) {
result.set(path_1.default.dirname(normalizedComponentEntryPath), entryType);
result.set(path_1.default.resolve(normalizedComponentEntryPath), entryType);
}
else {
result.set(normalizedComponentEntryPath, entryType);
Expand All @@ -575,21 +572,6 @@ const watchApp = (handoff) => __awaiter(void 0, void 0, void 0, function* () {
}
return result;
};
/*
if (fs.existsSync(path.resolve(handoff.workingPath, 'handoff.config.json'))) {
chokidar.watch(path.resolve(handoff.workingPath, 'handoff.config.json'), { ignoreInitial: true }).on('all', async (event, file) => {
console.log(chalk.yellow('handoff.config.json changed. Please restart server to see changes...'));
if (!debounce) {
debounce = true;
handoff.reload();
watchRuntimeComponents(getRuntimeComponentsPathsToWatch());
watchRuntimeConfiguration();
await processComponents(handoff, undefined, sharedStyles, documentationObject.components);
debounce = false;
}
});
}
*/
watchRuntimeComponents(getRuntimeComponentsPathsToWatch());
watchRuntimeConfiguration();
if (((_f = (_e = handoff.integrationObject) === null || _e === void 0 ? void 0 : _e.entries) === null || _f === void 0 ? void 0 : _f.integration) && fs_extra_1.default.existsSync((_h = (_g = handoff.integrationObject) === null || _g === void 0 ? void 0 : _g.entries) === null || _h === void 0 ? void 0 : _h.integration)) {
Expand Down
25 changes: 25 additions & 0 deletions dist/app/components/Component/Cards.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
/**
* Card interface for the Cards component
*/
export interface Card {
/** Title of the card */
title: string;
/** Content of the card (supports multi-line with \n separators) */
content: string;
/** (Optional) Visual type of the card affecting icon and colors */
type?: 'positive' | 'negative';
}
/**
* Props for the Cards component
*/
export interface CardsProps {
/** Array of cards to display */
cards: Card[];
/** Maximum number of cards per row (1-2, default: 2) */
maxCardsPerRow?: 1 | 2;
/** Additional CSS classes */
className?: string;
}
declare const Cards: React.FC<CardsProps>;
export default Cards;
62 changes: 62 additions & 0 deletions dist/app/components/Component/Cards.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const react_1 = __importDefault(require("react"));
const CardIcon = ({ type }) => {
switch (type) {
case 'positive':
return (<svg className="mt-[5px] h-3 w-3 flex-shrink-0 text-emerald-600" strokeWidth={3} fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7"/>
</svg>);
case 'negative':
return (<svg className="mt-[5px] h-3 w-3 flex-shrink-0 text-gray-400" strokeWidth={3} fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12"/>
</svg>);
default:
return <></>;
}
};
const CardItem = ({ card }) => {
// Split content by newlines and render each line as a separate item
const lines = card.content.split('\n').filter((line) => line.trim());
return (<ul className="space-y-3">
{lines.map((line, index) => (<li key={index} className="flex items-start gap-3">
<CardIcon type={card.type}/>
<p className="text-sm">{line}</p>
</li>))}
</ul>);
};
const Cards = ({ cards, maxCardsPerRow = 2, className = '' }) => {
if (!cards || cards.length === 0) {
return null;
}
// Calculate grid columns based on maxCardsPerRow (always full width, max 2 per row)
const getGridCols = () => {
if (maxCardsPerRow === 1)
return 'grid-cols-1';
if (maxCardsPerRow === 2)
return 'grid-cols-1 sm:grid-cols-2';
if (maxCardsPerRow === 3)
return 'grid-cols-1 sm:grid-cols-2'; // Cap at 2 per row
return 'grid-cols-1 sm:grid-cols-2'; // Default to max 2 per row
};
return (<div className={`flex flex-col gap-2 pb-7 ${className}`}>
<div className={`grid gap-6 ${getGridCols()}`}>
{cards.map((card, index) => (<div key={`card-${index}`} className={`relative rounded-lg border p-8 ${card.type === 'positive'
? 'bg-gray-50 text-gray-600 dark:bg-gray-800'
: card.type === 'negative'
? 'bg-gray-50 text-gray-600 dark:bg-gray-800'
: 'bg-gray-50 text-gray-600 dark:bg-gray-800'}`}>
<h2 className={`mb-3 font-normal ${card.type === 'positive' ? 'text-gray-700' : card.type === 'negative' ? 'text-gray-900' : 'text-gray-800'}`}>
{card.title}
</h2>
<div className={`${card.type === 'positive' ? 'text-emerald-800' : card.type === 'negative' ? 'text-red-800' : 'text-blue-800'}`}>
<CardItem card={card}/>
</div>
</div>))}
</div>
</div>);
};
exports.default = Cards;
Empty file modified dist/cli.js
100644 → 100755
Empty file.
2 changes: 1 addition & 1 deletion dist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ declare class Handoff {
private readJsonFile;
}
export declare const initIntegrationObject: (handoff: Handoff) => [integrationObject: IntegrationObject, configs: string[]];
export type { ComponentListObject as Component } from './transformers/preview/types';
export type { ComponentObject as Component } from './transformers/preview/types';
export type { Config } from './types/config';
export { Transformers as CoreTransformers, TransformerUtils as CoreTransformerUtils, Types as CoreTypes } from 'handoff-core';
export default Handoff;
2 changes: 1 addition & 1 deletion dist/transformers/preview/component/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export declare const getAPIPath: (handoff: Handoff) => string;
* @param componentData
*/
declare const writeComponentSummaryAPI: (handoff: Handoff, componentData: ComponentListObject[]) => Promise<void>;
export declare const writeComponentApi: (id: string, component: TransformComponentTokensResult, version: string, handoff: Handoff, isPartialUpdate?: boolean) => Promise<void>;
export declare const writeComponentApi: (id: string, component: TransformComponentTokensResult, version: string, handoff: Handoff, preserveKeys?: string[]) => Promise<void>;
export declare const writeComponentMetadataApi: (id: string, summary: ComponentListObject, handoff: Handoff) => Promise<void>;
/**
* Update the main component summary API with the new component data
Expand Down
65 changes: 46 additions & 19 deletions dist/transformers/preview/component/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,34 @@ Object.defineProperty(exports, "__esModule", { value: true });
exports.updateComponentSummaryApi = exports.writeComponentMetadataApi = exports.writeComponentApi = exports.getAPIPath = void 0;
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
function updateObject(target, source) {
return Object.entries(source).reduce((acc, [key, value]) => {
if (value !== undefined && value !== null && value !== '') {
acc[key] = value;
/**
* Merges values from a source object into a target object, returning a new object.
* For each key present in either object:
* - If the key is listed in preserveKeys and the source value is undefined, null, or an empty string,
* the target's value is preserved.
* - Otherwise, the value from the source is used (even if undefined, null, or empty string).
* This is useful for partial updates where some properties should not be overwritten unless explicitly set.
*
* @param target - The original object to merge into
* @param source - The object containing new values
* @param preserveKeys - Keys for which the target's value should be preserved if the source value is undefined, null, or empty string
* @returns A new object with merged values
*/
function updateObject(target, source, preserveKeys = []) {
// Collect all unique keys from both target and source
const allKeys = Array.from(new Set([
...Object.keys(target),
...Object.keys(source)
]));
return allKeys.reduce((acc, key) => {
const sourceValue = source[key];
const targetValue = target[key];
// Preserve existing values for specified keys when source value is undefined
if (preserveKeys.includes(key) && (sourceValue === undefined || sourceValue === null || sourceValue === '')) {
acc[key] = targetValue;
}
else {
acc[key] = sourceValue;
}
return acc;
}, Object.assign({}, target));
Expand All @@ -42,22 +66,25 @@ const writeComponentSummaryAPI = (handoff, componentData) => __awaiter(void 0, v
componentData.sort((a, b) => a.title.localeCompare(b.title));
yield fs_extra_1.default.writeFile(path_1.default.resolve((0, exports.getAPIPath)(handoff), 'components.json'), JSON.stringify(componentData, null, 2));
});
const writeComponentApi = (id_1, component_1, version_1, handoff_1, ...args_1) => __awaiter(void 0, [id_1, component_1, version_1, handoff_1, ...args_1], void 0, function* (id, component, version, handoff, isPartialUpdate = false) {
const writeComponentApi = (id_1, component_1, version_1, handoff_1, ...args_1) => __awaiter(void 0, [id_1, component_1, version_1, handoff_1, ...args_1], void 0, function* (id, component, version, handoff, preserveKeys = []) {
const outputDirPath = path_1.default.resolve((0, exports.getAPIPath)(handoff), 'component', id);
if (isPartialUpdate) {
const outputFilePath = path_1.default.resolve(outputDirPath, `${version}.json`);
if (fs_extra_1.default.existsSync(outputFilePath)) {
const existingJson = yield fs_extra_1.default.readFile(outputFilePath, 'utf8');
if (existingJson) {
try {
const existingData = JSON.parse(existingJson);
const mergedData = updateObject(existingData, component);
yield fs_extra_1.default.writeFile(path_1.default.resolve(outputDirPath, `${version}.json`), JSON.stringify(mergedData, null, 2));
return;
}
catch (_) {
// Unable to parse existing file
}
const outputFilePath = path_1.default.resolve(outputDirPath, `${version}.json`);
if (fs_extra_1.default.existsSync(outputFilePath)) {
const existingJson = yield fs_extra_1.default.readFile(outputFilePath, 'utf8');
if (existingJson) {
try {
const existingData = JSON.parse(existingJson);
// Special case: always allow page to be cleared when undefined
// This handles the case where page slices are removed
const finalPreserveKeys = component.page === undefined
? preserveKeys.filter(key => key !== 'page')
: preserveKeys;
const mergedData = updateObject(existingData, component, finalPreserveKeys);
yield fs_extra_1.default.writeFile(path_1.default.resolve(outputDirPath, `${version}.json`), JSON.stringify(mergedData, null, 2));
return;
}
catch (_) {
// Unable to parse existing file
}
}
}
Expand Down
34 changes: 32 additions & 2 deletions dist/transformers/preview/component/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,32 @@ var ComponentSegment;
ComponentSegment["Previews"] = "previews";
ComponentSegment["Validation"] = "validation";
})(ComponentSegment || (exports.ComponentSegment = ComponentSegment = {}));
/**
* Determines which keys should be preserved based on the segment being processed.
* When processing a specific segment, we want to preserve data from other segments
* to avoid overwriting them with undefined values.
*/
function getPreserveKeysForSegment(segmentToProcess) {
if (!segmentToProcess) {
return []; // No preservation needed for full updates
}
switch (segmentToProcess) {
case ComponentSegment.JavaScript:
// When processing JavaScript segment, preserve CSS and previews data
return ['css', 'sass', 'sharedStyles', 'previews', 'validations'];
case ComponentSegment.Style:
// When processing Style segment, preserve JavaScript and previews data
return ['js', 'jsCompiled', 'previews', 'validations'];
case ComponentSegment.Previews:
// When processing Previews segment, preserve JavaScript and CSS data
return ['js', 'jsCompiled', 'css', 'sass', 'sharedStyles', 'validations'];
case ComponentSegment.Validation:
// When processing Validation segment, preserve all other data
return ['js', 'jsCompiled', 'css', 'sass', 'sharedStyles', 'previews'];
default:
return [];
}
}
/**
* Process components and generate their code, styles, and previews
* @param handoff - The Handoff instance containing configuration and state
Expand All @@ -83,6 +109,10 @@ function processComponents(handoff, id, segmentToProcess) {
const components = (yield handoff.getDocumentationObject()).components;
const sharedStyles = yield handoff.getSharedStyles();
const runtimeComponents = (_c = (_b = (_a = handoff.integrationObject) === null || _a === void 0 ? void 0 : _a.entries) === null || _b === void 0 ? void 0 : _b.components) !== null && _c !== void 0 ? _c : {};
// Determine which keys to preserve based on the segment being processed
// This ensures that when processing only specific segments (e.g., JavaScript only),
// we don't overwrite data from other segments (e.g., CSS, previews) with undefined values
const preserveKeys = getPreserveKeysForSegment(segmentToProcess);
for (const runtimeComponentId of Object.keys(runtimeComponents)) {
if (!!id && runtimeComponentId !== id) {
continue;
Expand All @@ -109,13 +139,13 @@ function processComponents(handoff, id, segmentToProcess) {
data.validations = validationResults;
}
data.sharedStyles = sharedStyles;
yield (0, api_1.writeComponentApi)(runtimeComponentId, data, version, handoff, true);
yield (0, api_1.writeComponentApi)(runtimeComponentId, data, version, handoff, preserveKeys);
if (version === latest) {
latestVersion = data;
}
})));
if (latestVersion) {
yield (0, api_1.writeComponentApi)(runtimeComponentId, latestVersion, 'latest', handoff, true);
yield (0, api_1.writeComponentApi)(runtimeComponentId, latestVersion, 'latest', handoff, preserveKeys);
const summary = buildComponentSummary(runtimeComponentId, latestVersion, versions);
yield (0, api_1.writeComponentMetadataApi)(runtimeComponentId, summary, handoff);
result.push(summary);
Expand Down
1 change: 1 addition & 0 deletions dist/transformers/preview/component/css.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ declare const buildComponentCss: (data: TransformComponentTokensResult, handoff:
};
};
validations?: Record<string, import("../../../types").ValidationResult>;
page?: import("../types").ComponentPageDefinition;
}>;
/**
* Build the main CSS file using Vite
Expand Down
3 changes: 0 additions & 3 deletions dist/transformers/preview/component/json.d.ts

This file was deleted.

57 changes: 0 additions & 57 deletions dist/transformers/preview/component/json.js

This file was deleted.

Loading