Package Exports
- bleakai
- bleakai/dist/index.es.js
- bleakai/dist/index.umd.js
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (bleakai) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
bleakai - Framework-Agnostic Bleak Element Component Resolver
A truly framework-agnostic library that handles the logic of determining which component to use for dynamic bleak elements. No rendering, no framework dependencies - just pure component resolution logic.
🌟 Features
- Zero Framework Dependencies: Works with React, Vue, Angular, Svelte, plain JS, anything
- Pure Logic: Only handles component selection - you handle rendering
- Type Safe: Full TypeScript support
- Tiny: Minimal bundle size with no framework bloat
- Intuitive: Simple API that's easy to understand and use
📦 Installation
npm install bleakai
🚀 Quick Start
The Core Concept
- Define your config following the library's type structure (single source of truth)
- Use the library's helper to create a resolver from your config
- Get components and props directly - no manual mapping needed!
import {createResolverFromConfig, type BleakElementConfig} from "bleakai";
import {TextInput, RadioGroup, TextArea} from "./my-components";
// 1. Define your config - enforced by library types!
const BLEAK_CONFIG = {
text: {
component: TextInput,
description: "Use for open-ended text input"
},
radio: {
component: RadioGroup,
description: "Use for single choice from options"
},
textarea: {
component: TextArea,
description: "Use for longer text input"
}
} satisfies BleakElementConfig;
// 2. Create resolver from config (super smooth!)
const {resolve} = createResolverFromConfig(BLEAK_CONFIG);
// 3. Use it - get Component and props directly!
function DynamicBleakElement({element, value, onChange}) {
const {Component, props} = resolve(element, value, onChange);
return <Component {...props} />;
}
Why This Approach Rocks
✅ Single source of truth - your config defines everything
✅ Type enforced - library ensures you follow the right structure
✅ No manual mapping - library handles component resolution
✅ Framework agnostic - same pattern works everywhere
🎯 Framework Examples
React
import {createResolver} from "bleakai";
import {TextInput, RadioGroup, TextArea} from "./my-components";
// Your component registry
const components = {
TextInput,
RadioGroup,
TextArea
};
// Component map for the resolver
const componentMap = {
text: "TextInput",
radio: "RadioGroup",
textarea: "TextArea"
};
function DynamicBleakElement({element, value, onChange}) {
const resolver = createResolver(componentMap);
const {componentKey, props} = resolver.resolve(element, value, onChange);
// Get the actual component from your registry
const Component = components[componentKey];
return <Component {...props} />;
}
// Usage
function App() {
const [value, setValue] = useState("");
const element = {type: "text", text: "Enter your name:"};
return (
<DynamicBleakElement element={element} value={value} onChange={setValue} />
);
}
Vue
<template>
<component :is="resolvedComponent" v-bind="resolvedProps" />
</template>
<script setup>
import {computed} from "vue";
import {createResolver} from "bleakai";
import TextInput from "./TextInput.vue";
import RadioGroup from "./RadioGroup.vue";
const props = defineProps(["element", "value", "onChange"]);
// Your component registry
const components = {
TextInput,
RadioGroup
};
// Component map
const componentMap = {
text: "TextInput",
radio: "RadioGroup"
};
const resolver = createResolver(componentMap);
const resolved = computed(() =>
resolver.resolve(props.element, props.value, props.onChange)
);
const resolvedComponent = computed(
() => components[resolved.value.componentKey]
);
const resolvedProps = computed(() => resolved.value.props);
</script>
Vanilla JavaScript
import {createResolver} from "bleakai";
// Your component factory functions
const components = {
TextInput: (props) => {
const input = document.createElement("input");
input.type = "text";
input.value = props.value;
input.placeholder = props.text;
input.addEventListener("input", (e) => props.onChange(e.target.value));
return input;
},
RadioGroup: (props) => {
const container = document.createElement("div");
container.innerHTML = `<label>${props.text}</label>`;
props.options?.forEach((option) => {
const label = document.createElement("label");
const radio = document.createElement("input");
radio.type = "radio";
radio.value = option;
radio.checked = props.value === option;
radio.addEventListener("change", () => props.onChange(option));
label.appendChild(radio);
label.appendChild(document.createTextNode(option));
container.appendChild(label);
});
return container;
}
};
// Component map
const componentMap = {
text: "TextInput",
radio: "RadioGroup"
};
// Usage
function renderBleakElement(targetElement, element, value, onChange) {
const resolver = createResolver(componentMap);
const {componentKey, props} = resolver.resolve(element, value, onChange);
// Get the factory function and create the element
const factory = components[componentKey];
const domElement = factory(props);
targetElement.innerHTML = "";
targetElement.appendChild(domElement);
}
// Example usage
const targetEl = document.getElementById("element-container");
const element = {type: "text", text: "Enter your email:"};
renderBleakElement(targetEl, element, "", (value) =>
console.log("Value:", value)
);
🎨 Advanced Usage
Batch Processing
import {resolveElements} from "bleakai";
const elements = [
{type: "text", text: "Name?"},
{type: "radio", text: "Experience?", options: ["Beginner", "Expert"]}
];
const values = {"Name?": "John", "Experience?": "Expert"};
const resolved = resolveElements(
elements,
values,
(elementText, value) => console.log(elementText, "=", value),
{text: "TextInput", radio: "RadioGroup"}
);
// Now render each resolved component in your framework
resolved.forEach(({componentKey, props}, index) => {
// Your rendering logic here
});
Custom Resolver with Options
import {BleakResolver} from "bleakai";
const resolver = new BleakResolver({
components: {
text: "TextInput",
radio: "RadioGroup",
slider: "SliderInput"
},
fallbackComponent: "TextInput",
shouldHaveOptions: (type) => ["radio", "select"].includes(type),
getDefaultOptions: (type) => (type === "radio" ? ["Yes", "No"] : []),
logger: {
onResolve: (type, componentKey) =>
console.log(`Resolved ${type} to ${componentKey}`),
onFallback: (type, fallback, reason) =>
console.warn(`Fallback for ${type}: ${reason}`)
}
});
const result = resolver.resolve({type: "unknown", text: "Test?"}, "", () => {});
// Will use fallback component
🔧 API Reference
Types
interface BleakElement {
type: string;
text: string;
options?: string[] | null;
}
interface BleakElementProps {
text: string;
value: string;
onChange: (value: string) => void;
elementIndex?: number;
options?: string[];
}
interface ComponentResolution {
type: string;
componentKey: string;
props: BleakElementProps;
}
Functions
createResolver(componentMap, options?)
- Quick resolver setupresolveElement(element, value, onChange, componentMap)
- One-off resolutionresolveElements(elements, values, onChange, componentMap)
- Batch resolutioncreateStandardComponentMap(customComponents?)
- Helper for common component types
Classes
BleakResolver
- Main resolver class with full configuration options
🛠️ Local Development & Testing
Building and Linking Locally
If you want to test local changes to this library in your project:
Build the library:
cd library npm run build
Create a local npm link:
npm link
In your project, link to the local version:
cd your-project npm link bleakai
After making changes, rebuild and test:
cd library npm run build # Changes will be automatically available in your linked project
Testing the Build
To test that the library builds correctly and all exports work:
cd library
npm run build
npm run test # If you have tests set up
Unlinking
When you're done with local development:
# In your project
npm unlink bleakai
# In the library
npm unlink
# Then reinstall the published version
cd your-project
npm install bleakai
Development Workflow
- Make changes to the library code
- Run
npm run build
in the library directory - Test in your linked project
- Repeat until satisfied
- Commit and publish when ready
Common Issues
- Changes not reflecting: Make sure to rebuild (
npm run build
) after code changes - Type errors: Ensure TypeScript is generating declaration files correctly
- Import errors: Check that the
package.json
exports are configured properly
💡 Why This Approach?
- True Framework Agnostic: No framework dependencies at all
- Separation of Concerns: Logic vs. rendering are completely separate
- Lightweight: Tiny bundle size
- Flexible: Use any component library, any rendering approach
- Type Safe: Full TypeScript support
- Intuitive: Easy to understand and debug
📄 License
MIT License - see LICENSE file for details.