Skip to content
Merged
2 changes: 1 addition & 1 deletion .github/skills/syncfusion-blazor-toolkit-buttons/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ description: >
navigation menu UI (use SfMenu / SfToolbar — not in this skill),
icon-only controls that should suppress focus (use a styled
syncfusion-blazor-toolkit-notifications spinner instead).
compatibility: .NET 8+, render-modes: Static SSR, Server, WebAssembly, Auto
compatibility: ".NET 8+, render-modes: Static SSR, Server, WebAssembly, Auto"
metadata:
author: "Syncfusion Inc"
version: "1.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ description: >
syncfusion-blazor-toolkit-inputs), scheduling or calendar recurrence
(use Scheduler — not in this skill), or date validation that requires an
API round-trip (handle in EditContext validator instead).
compatibility: .NET 8+, render-modes: Server, WebAssembly, Auto
compatibility: ".NET 8+, render-modes: Server, WebAssembly, Auto"
metadata:
author: "Syncfusion Inc"
version: "1.0.0"
Expand Down
2 changes: 1 addition & 1 deletion .github/skills/syncfusion-blazor-toolkit-inputs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ description: >
DO NOT USE FOR: button controls (use syncfusion-blazor-toolkit-buttons),
rich text editing (use SfRichTextEditor — not in this skill), date or
time inputs (use syncfusion-blazor-toolkit-calendars).
compatibility: .NET 8+, render-modes: Static SSR (text/checkbox/numeric/Switch); Server, WebAssembly, Auto (all)
compatibility: ".NET 8+, render-modes: Static SSR (text/checkbox/numeric/Switch); Server, WebAssembly, Auto (all)"
metadata:
author: "Syncfusion Inc"
version: "1.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ description: >
this skill), action-bearing toast notifications (use SfToast — not in
this skill), or progress bars with explicit percent (use
SfProgressBar — not in this skill).
compatibility: .NET 8+, render-modes: Server, WebAssembly, Auto
compatibility: ".NET 8+, render-modes: Server, WebAssembly, Auto"
metadata:
author: "Syncfusion Inc"
version: "1.0.0"
Expand Down
121 changes: 103 additions & 18 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ var shelljs = global.shelljs = global.shelljs || require('shelljs');
var gulp = global.gulp = global.gulp || require('gulp');
const glob = require('glob');
const sass = require('gulp-sass')(require('sass'));
const cleanCSS = require('gulp-clean-css');
const rename = require('gulp-rename');

var componentThemeOrder = [
Expand Down Expand Up @@ -64,6 +63,7 @@ function removeCustomUse(fileContent) {
gulp.task('combined-scss', function (done) {
// Get the all components scss files' path
var componentFiles = glob.sync(`./src/wwwroot/styles/*.scss`);
shelljs.mkdir('-p', './src/wwwroot/styles/combined-scss/');
var getFluentScss = '';
// Place component styles as per styles order
for (var themeOrder of componentThemeOrder) {
Expand All @@ -75,35 +75,120 @@ gulp.task('combined-scss', function (done) {
}
}
getFluentScss = removeCustomUse(getFluentScss);
shelljs.mkdir('-p', './src/wwwroot/styles/combined-scss/');
fs.writeFileSync('./src/wwwroot/styles/combined-scss/fluent.scss', reorderUseRules(getFluentScss), 'utf8');
var hcBody = '';
for (var hcOrder of componentThemeOrder) {
var hcPaths = componentFiles.filter((value) => { return value.indexOf('styles/' + hcOrder) !== -1; });
if (!hcPaths.length) continue;
var content = stripBom(fs.readFileSync(hcPaths[0], 'utf8'));
if (hcOrder === 'base') content = stripRootScopes(content);
hcBody += '\n' + content;
}
hcBody = removeCustomUse(hcBody);
hcBody = reorderUseRules(hcBody);

var tokensSrc = stripBom(fs.readFileSync('./src/wwwroot/styles/highcontrast-tokens.scss', 'utf8'));
var unlayeredMarker = '// Unscoped component overrides (component-state corrections that var() tokens';
var markerIdx = tokensSrc.indexOf(unlayeredMarker);
var unlayeredPostlude = '';
if (markerIdx >= 0) {
var forcedIdx = tokensSrc.indexOf('@media (forced-colors: active)', markerIdx);
var endIdx = forcedIdx > markerIdx ? forcedIdx : tokensSrc.length;
unlayeredPostlude = tokensSrc.substring(markerIdx, endIdx).trim() + '\n';
}

fs.writeFileSync(
'./src/wwwroot/styles/combined-scss/highcontrast.scss',
"@use 'highcontrast-tokens';\n" + hcBody + '\n' + unlayeredPostlude,
'utf8'
);

fs.copyFileSync(
'./src/wwwroot/styles/highcontrast-tokens.scss',
'./src/wwwroot/styles/combined-scss/_highcontrast-tokens.scss'
);
done();
});

function stripBom(content) {
return content.replace(/^\uFEFF/, '');
}

function stripRootScopes(content) {
var out = '', i = 0;
while (i < content.length) {
// Skip a preceding @layer <name> { ... } block if it's a theme
// layer or contains a :root{} rule.
var layerSearch = content.slice(i).search(/(^|[;}]\s*)@layer\s+[a-zA-Z][\w.]*\s*\{/);
if (layerSearch >= 0) {
var headerMatch = content.slice(i + layerSearch).match(/@layer\s+([a-zA-Z][\w.]*)\s*\{/);
if (headerMatch) {
var openBrace = i + layerSearch + headerMatch.index + headerMatch[0].length - 1;
var end = openBrace, depth = 1;
while (++end < content.length && depth > 0) {
if (content[end] === '{') depth++;
else if (content[end] === '}') depth--;
}
var body = content.slice(i + layerSearch, end);
if (/fluent|themes/.test(headerMatch[1]) || /:root\s*\{/.test(body)) {
out += content.slice(i, i + layerSearch);
i = end;
continue;
}
out += content.slice(i, end);
i = end;
continue;
}
}
// Process the next rule
var brace = content.indexOf('{', i);
if (brace === -1) { out += content.slice(i); break; }
var selStart = Math.max(content.lastIndexOf(';', brace), content.lastIndexOf('}', brace), 0) + 1;
while (selStart < brace && /\s/.test(content[selStart])) selStart++;
var selector = content.slice(selStart, brace).trim();
var depth = 1, j = brace + 1;
while (j < content.length && depth > 0) {
if (content[j] === '{') depth++;
else if (content[j] === '}') depth--;
if (depth === 0) break;
j++;
}
if (/^:root\b/.test(selector) || /\.e-dark-mode\b/.test(selector)) {
out += content.slice(i, selStart);
} else {
out += content.slice(i, j + 1);
}
i = j + 1;
}
return out;
}

// Compile SCSS to CSS.
gulp.task('scss-to-css', function (done) {
function cleanup() {
try { fs.unlinkSync('./src/wwwroot/styles/combined-scss/_highcontrast-tokens.scss'); } catch (e) { }
console.log("SCSS to CSS compiled successfully");
done();
}
return gulp.src(
['./src/wwwroot/styles/combined-scss/*.scss', './src/wwwroot/styles/*.scss'],
{ ignore: ['./src/wwwroot/styles/icons.scss', './src/wwwroot/styles/animation.scss', './src/wwwroot/styles/base.scss'] }
) // Select all SCSS files in the directory for compiling to css except base and icons scss
.pipe(sass().on('error', function (error) {
// Handle SCSS compilation errors
fs.appendFileSync('./gulp_error.log', 'Failed scss-to-css task \nDetails:\n' + error.message + '\n');
console.error('Sass Compilation Error:', error.messageFormatted);
process.exit(1);
}))
// Minify and write only the .min.css files
.pipe(cleanCSS())
.pipe(rename({ suffix: '.min' }))
.pipe(gulp.dest('./src/wwwroot/styles'))
.on('end', function () {
console.log("SCSS to CSS compiled successfully");
done();
});
{ ignore: [
'./src/wwwroot/styles/icons.scss',
'./src/wwwroot/styles/animation.scss',
'./src/wwwroot/styles/base.scss',
'./src/wwwroot/styles/highcontrast-tokens.scss',
'./src/wwwroot/styles/combined-scss/_highcontrast-tokens.scss'
] }
)
.pipe(sass({ outputStyle: 'compressed' }).on('error', function (error) {
fs.appendFileSync('./gulp_error.log', 'Failed scss-to-css task\n' + error.message + '\n');
console.error('Sass Compilation Error:', error.messageFormatted);
process.exit(1);
}))
.pipe(rename({ suffix: '.min' }))
.pipe(gulp.dest('./src/wwwroot/styles'))
.on('end', cleanup)
.on('error', cleanup);
});

gulp.task('blazor-toolkit-themes', gulp.series('combined-scss', 'scss-to-css'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,14 +213,14 @@
<EditForm Model="@_annotation" OnValidSubmit="@OnValidSubmit" OnInvalidSubmit="@OnInvalidSubmit">
<DataAnnotationsValidator />
<div class="form-group">
<SfTextBox @bind-Value="@_annotation.Name" FloatLabelType="FloatLabelType.Auto"
Placeholder="Enter a Name"></SfTextBox>
<ValidationMessage For="@(() => _annotation.Name)" />
<SfTextBox @bind-Value="@_annotation.Name" id="tb-name"
FloatLabelType="FloatLabelType.Auto" Placeholder="Enter a Name"></SfTextBox>
<ValidationMessage For="@(() => _annotation.Name)" id="err_tb-name" />
<div class="example-content">
<SfTextArea @bind-Value="@_annotation.Comments" RowCount="5"
<SfTextArea @bind-Value="@_annotation.Comments" id="tb-comments" RowCount="5"
FloatLabelType="FloatLabelType.Auto" Placeholder="Enter your Comments"
ColumnCount="250"></SfTextArea>
<ValidationMessage For="@(() => _annotation.Comments)" />
<ValidationMessage For="@(() => _annotation.Comments)" id="err_tb-comments" />
</div>
</div>
<div class="d-flex justify-content-center gap-3 mt-4">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,20 +256,20 @@
<EditForm Model="@validationModel" OnValidSubmit="@OnValidSubmit" FormName="TextBoxValidationForm">
<DataAnnotationsValidator></DataAnnotationsValidator>
<div class="form-group">
<SfTextBox @bind-Value="@validationModel.FullName" Placeholder="Full Name (Required)" FloatLabelType="FloatLabelType.Auto"></SfTextBox>
<ValidationMessage For="@(() => validationModel.FullName)"></ValidationMessage>
<SfTextBox @bind-Value="@validationModel.FullName" id="tb-fullname" Placeholder="Full Name (Required)" FloatLabelType="FloatLabelType.Auto"></SfTextBox>
<ValidationMessage For="@(() => validationModel.FullName)" id="err_tb-fullname"></ValidationMessage>
</div>
<div class="form-group">
<SfTextBox @bind-Value="@validationModel.Email" Type="InputType.Email" Placeholder="Email Address" FloatLabelType="FloatLabelType.Auto"></SfTextBox>
<ValidationMessage For="@(() => validationModel.Email)"></ValidationMessage>
<SfTextBox @bind-Value="@validationModel.Email" id="tb-email" Type="InputType.Email" Placeholder="Email Address" FloatLabelType="FloatLabelType.Auto"></SfTextBox>
<ValidationMessage For="@(() => validationModel.Email)" id="err_tb-email"></ValidationMessage>
</div>
<div class="form-group">
<SfTextBox @bind-Value="@validationModel.Phone" Type="InputType.Tel" Placeholder="Phone Number" FloatLabelType="FloatLabelType.Auto"></SfTextBox>
<ValidationMessage For="@(() => validationModel.Phone)"></ValidationMessage>
<SfTextBox @bind-Value="@validationModel.Phone" id="tb-phone" Type="InputType.Tel" Placeholder="Phone Number" FloatLabelType="FloatLabelType.Auto"></SfTextBox>
<ValidationMessage For="@(() => validationModel.Phone)" id="err_tb-phone"></ValidationMessage>
</div>
<div class="form-group">
<SfTextBox @bind-Value="@validationModel.Comments" Placeholder="Comments (Max 200 characters)" Multiline="true" FloatLabelType="FloatLabelType.Auto" InputAttributes="@validationAttrs"></SfTextBox>
<ValidationMessage For="@(() => validationModel.Comments)"></ValidationMessage>
<SfTextBox @bind-Value="@validationModel.Comments" id="tb-comments" Placeholder="Comments (Max 200 characters)" Multiline="true" FloatLabelType="FloatLabelType.Auto" InputAttributes="@validationAttrs"></SfTextBox>
<ValidationMessage For="@(() => validationModel.Comments)" id="err_tb-comments"></ValidationMessage>
</div>
<div class="form-buttons">
<button type="submit" class="e-btn e-primary">Submit</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,6 @@
SequentialUpload="false"
ShowFileList="true"
ShowProgressBar="false"
TabIndex="1"
FileSelected="OnFileSelected"
BeforeUpload="OnBeforeUpload"
BeforeRemove="OnBeforeRemove"
Expand Down Expand Up @@ -445,7 +444,6 @@
SequentialUpload=""false""
ShowFileList=""true""
ShowProgressBar=""false""
TabIndex=""1""
FileSelected=""OnFileSelected""
BeforeUpload=""OnBeforeUpload""
BeforeRemove=""OnBeforeRemove""
Expand Down
1 change: 1 addition & 0 deletions src/Components/Inputs/CheckBox/SfCheckBox.razor
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
value="@Value"
disabled="@Disabled"
checked="@GetIsChecked()"
@onclick:preventDefault="true"
aria-label="@GetAriaLabelValue()"
aria-readonly="@GetAriaReadOnly()"
aria-checked="@GetAriaChecked()"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ protected override async Task OnInitializedAsync()
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender).ConfigureAwait(false);
await InvokeVoidAsync(_checkBoxJsModule, _checkBoxInProcessModule, "syncIndeterminate", _input, Indeterminate).ConfigureAwait(true);
if (firstRender && EnablePersistence)
{
try
Expand Down
10 changes: 5 additions & 5 deletions src/Components/Inputs/CheckBox/SfCheckBox.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ private Dictionary<string, object> GetAttributes(object titleValue)
/// <returns>The next <see cref="CheckboxState"/> to transition to.</returns>
/// <remarks>
/// <para>
/// When tri-state is enabled, the cycle is: Checked → IndeterminateUnchecked → Checked.
/// When tri-state is enabled, the cycle is: Checked → UncheckedIndeterminate → Checked.
/// </para>
/// <para>
/// When tri-state is disabled, the cycle is: Checked ⇄ Unchecked (standard two-state toggle).
Expand All @@ -480,10 +480,10 @@ private static CheckboxState DetermineNextState(bool isChecked, bool isIndetermi
{
if (allowTriState)
{
// Tri-state cycle: Checked → IndeterminateUnchecked → Checked
return isChecked && !isIndeterminate
? CheckboxState.Indeterminate
: isIndeterminate ? CheckboxState.Unchecked : CheckboxState.Checked;
// Tri-state cycle: Checked → UncheckedIndeterminate → Checked
return isIndeterminate
? CheckboxState.Checked
: isChecked ? CheckboxState.Unchecked : CheckboxState.Indeterminate;
}

// Two-state toggle: handle indeterminate as a transition state
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ protected override async Task OnParametersSetAsync()
await base.OnParametersSetAsync().ConfigureAwait(true);
UpdateValidateClass();
InputHtmlAttributes = SfBaseUtils.UpdateDictionary(ROLE, SPIN_BUTTON, InputHtmlAttributes);
InputHtmlAttributes = SfBaseUtils.UpdateDictionary(ARIA_LIVE, ASSERTIVE, InputHtmlAttributes);
//InputHtmlAttributes = SfBaseUtils.UpdateDictionary(ARIA_LIVE, ASSERTIVE, InputHtmlAttributes);
if (!InputHtmlAttributes.ContainsKey(ARIA_LABEL) && !string.IsNullOrWhiteSpace(AriaLabel))
{
// When AriaLabel is explicitly supplied, forward it. We deliberately do NOT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -573,6 +573,10 @@ private async Task ChangeValueAsync(TValue? value)
/// </remarks>
private void UpdateValueAnnouncement()
{
if (IsFocus)
{
return;
}
string? formatted = FormatValueAsString(InputTextValue);
if (string.IsNullOrEmpty(formatted) || string.Equals(formatted, _lastAnnouncedFormattedValue, StringComparison.Ordinal))
{
Expand Down Expand Up @@ -1031,6 +1035,7 @@ protected override async Task FocusOutHandlerAsync(FocusEventArgs args)
else
{
IsFocus = false;
_lastAnnouncedFormattedValue = null;
string? inputValue = FocusInputValue;
if (string.IsNullOrEmpty(inputValue))
{
Expand Down
8 changes: 1 addition & 7 deletions src/Components/Inputs/Switch/SfSwitch.razor
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
role="switch"
aria-checked="@GetAriaPressed()"
aria-disabled="@Disabled.ToString().ToLower()"
aria-labelledby="@(!string.IsNullOrWhiteSpace(Label) ? $"{_idValue}-label" : null)"
aria-label="@(string.IsNullOrWhiteSpace(Label) ? (string.IsNullOrWhiteSpace(AriaLabel) ? null : AriaLabel) : null)"
aria-label="@GetAccessibleName()"
name="@Name"
value="@Value"
checked="@Checked"
Expand All @@ -34,9 +33,4 @@
</span>

<span class="@_handleClass"></span>

@if (!string.IsNullOrWhiteSpace(Label))
{
<label id="@($"{_idValue}-label")" class="e-switch-label" for="@_idValue">@Label</label>
}
</div>
Loading
Loading