SchemaViewer
Domain · API DocInteractive JSON Schema tree viewer with type coloring, constraint display, and collapsible nodes.
id*
string
UUID identifier
name*
string
Display name
price*
number
<%- include('modules/domain/api-doc/SchemaViewer', {
schema: {
type: 'object',
required: ['id', 'name', 'price'],
properties: {
id: { type: 'string', description: 'UUID identifier', readOnly: true },
name: { type: 'string', description: 'Display name' },
price: { type: 'number' },
}
}
}) %>
<%
var _schema = locals.schema || {};
var _title = locals.title || null;
var _name = locals.name || null;
var _required = !!locals.required;
var _depth = typeof locals.depth === 'number' ? locals.depth : 0;
var _defaultOpen = typeof locals.defaultOpen === 'boolean' ? locals.defaultOpen : (_depth === 0);
var _renderRoot = locals.renderRoot !== false; // top-level wrapper
var TYPE_COLORS = {
string: 'text-green-600 dark:text-green-400',
number: 'text-blue-600 dark:text-blue-400',
integer: 'text-blue-600 dark:text-blue-400',
boolean: 'text-purple-600 dark:text-purple-400',
array: 'text-orange-600 dark:text-orange-400',
object: 'text-yellow-600 dark:text-yellow-400',
'null': 'text-text-disabled',
};
function refName(ref) {
if (!ref) return null;
var parts = ref.split('/');
return parts[parts.length - 1];
}
var type = _schema.type || (_schema.properties ? 'object' : (_schema.items ? 'array' : null));
var typeLabel = _schema.enum ? 'enum' : (type || refName(_schema.$ref) || '?');
var typeColor = TYPE_COLORS[type || ''] || 'text-text-secondary';
var hasChildren =
(type === 'object' && _schema.properties) ||
(type === 'array' && _schema.items);
var enumValues = _schema.enum || null;
var nodeUid = 'sv_' + Math.random().toString(36).slice(2, 10);
%>
<% if (_renderRoot) { %>
<% if (_title) { %>
<%= _title %>
<% } %>
<% } %>
data-sv-toggle="<%= nodeUid %>"<% } %>
>
<% if (hasChildren) { %>
<% } else { %>
<% } %>
<% if (_name) { %>
<%= _name %><% if (_required) { %>*<% } %>
<% } %>
<%= typeLabel %><% if (_schema.format) { %>(<%= _schema.format %>)<% } %><% if (type === 'array' && _schema.items) { %>[<%= _schema.items.type || '...' %>]<% } %>
<% if (_schema.nullable) { %><% } %>
<% if (_schema.readOnly) { %><% } %>
<% if (_schema.writeOnly) { %><% } %>
<% if (_schema.deprecated) { %>deprecated<% } %>
<% if (_schema.description) { %>
<%= _schema.description %>
<% } %>
<% if (_schema.default !== undefined) { %>
default: <%= JSON.stringify(_schema.default) %>
<% } %>
<% if (enumValues && enumValues.length) { %>
<% enumValues.forEach(function(v) { %>
<%= JSON.stringify(v) %>
<% }); %>
<% } %>
<% if (_schema.minLength != null || _schema.maxLength != null || _schema.pattern) { %>
<% if (_schema.minLength != null) { %>minLength: <%= _schema.minLength %><% } %>
<% if (_schema.maxLength != null) { %>maxLength: <%= _schema.maxLength %><% } %>
<% if (_schema.pattern) { %>pattern: <%= _schema.pattern %><% } %>
<% } %>
<% if (_schema.minimum != null || _schema.maximum != null || _schema.multipleOf != null) { %>
<% if (_schema.minimum != null) { %>min: <%= _schema.minimum %><% } %>
<% if (_schema.maximum != null) { %>max: <%= _schema.maximum %><% } %>
<% if (_schema.multipleOf != null) { %>multipleOf: <%= _schema.multipleOf %><% } %>
<% } %>
<% if (_schema.allOf) { %>
allOf
<% _schema.allOf.forEach(function(s) { %>
<%- include('./SchemaViewer', { schema: s, depth: _depth + 1, defaultOpen: false, renderRoot: false }) %>
<% }); %>
<% } %>
<% if (_schema.anyOf) { %>
anyOf
<% _schema.anyOf.forEach(function(s) { %>
<%- include('./SchemaViewer', { schema: s, depth: _depth + 1, defaultOpen: false, renderRoot: false }) %>
<% }); %>
<% } %>
<% if (_schema.oneOf) { %>
oneOf
<% _schema.oneOf.forEach(function(s) { %>
<%- include('./SchemaViewer', { schema: s, depth: _depth + 1, defaultOpen: false, renderRoot: false }) %>
<% }); %>
<% } %>
<% if (hasChildren) { %>
>
<% if (type === 'object' && _schema.properties) {
var reqList = _schema.required || [];
Object.keys(_schema.properties).forEach(function(k) {
var child = _schema.properties[k];
var isReq = reqList.indexOf(k) !== -1;
%>
<%- include('./SchemaViewer', { schema: child, name: k, required: isReq, depth: _depth + 1, defaultOpen: false, renderRoot: false }) %>
<% });
} else if (type === 'array' && _schema.items) { %>
<%- include('./SchemaViewer', { schema: _schema.items, depth: _depth + 1, defaultOpen: false, renderRoot: false }) %>
<% } %>
<% } %>
<% if (_renderRoot) { %>
<% } %>